Skip to content

Commit 6543048

Browse files
committed
Change narrowing rules for parameter initialisers
They don't narrow the parameter type, except to remove undefined, and only if the initialiser type doesn't include undefined.
1 parent 61c742a commit 6543048

File tree

3 files changed

+54
-11
lines changed

3 files changed

+54
-11
lines changed

src/compiler/binder.ts

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -810,7 +810,7 @@ namespace ts {
810810
};
811811
}
812812

813-
function createFlowAssignment(antecedent: FlowNode, node: Expression | VariableDeclaration | BindingElement | ParameterDeclaration): FlowNode {
813+
function createFlowAssignment(antecedent: FlowNode, node: Expression | VariableDeclaration | BindingElement): FlowNode {
814814
setFlowNodeReferenced(antecedent);
815815
return <FlowAssignment>{
816816
flags: FlowFlags.Assignment,
@@ -819,6 +819,15 @@ namespace ts {
819819
};
820820
}
821821

822+
function createFlowInitializedParameter(antecedent: FlowNode, node: ParameterDeclaration): FlowNode {
823+
setFlowNodeReferenced(antecedent);
824+
return <FlowInitializedParameter>{
825+
flags: FlowFlags.InitializedParameter,
826+
antecedent,
827+
node
828+
};
829+
}
830+
822831
function createFlowArrayMutation(antecedent: FlowNode, node: CallExpression | BinaryExpression): FlowNode {
823832
setFlowNodeReferenced(antecedent);
824833
return <FlowArrayMutation>{
@@ -2312,7 +2321,7 @@ namespace ts {
23122321
else {
23132322
declareSymbolAndAddToSymbolTable(node, SymbolFlags.FunctionScopedVariable, SymbolFlags.ParameterExcludes);
23142323
if (node.initializer) {
2315-
currentFlow = createFlowAssignment(currentFlow, node);
2324+
currentFlow = createFlowInitializedParameter(currentFlow, node);
23162325
}
23172326
}
23182327

src/compiler/checker.ts

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9060,8 +9060,8 @@ namespace ts {
90609060
switch (source.kind) {
90619061
case SyntaxKind.Identifier:
90629062
return target.kind === SyntaxKind.Identifier && getResolvedSymbol(<Identifier>source) === getResolvedSymbol(<Identifier>target) ||
9063-
(target.kind === SyntaxKind.VariableDeclaration || target.kind === SyntaxKind.BindingElement) && getExportSymbolOfValueSymbolIfExported(getResolvedSymbol(<Identifier>source)) === getSymbolOfNode(target) ||
9064-
target.kind === SyntaxKind.Parameter && getExportSymbolOfValueSymbolIfExported(getResolvedSymbol(source as Identifier)) === getSymbolOfNode(target);
9063+
(target.kind === SyntaxKind.VariableDeclaration || target.kind === SyntaxKind.BindingElement || target.kind === SyntaxKind.Parameter) &&
9064+
getExportSymbolOfValueSymbolIfExported(getResolvedSymbol(source as Identifier)) === getSymbolOfNode(target);
90659065
case SyntaxKind.ThisKeyword:
90669066
return target.kind === SyntaxKind.ThisKeyword;
90679067
case SyntaxKind.PropertyAccessExpression:
@@ -9155,6 +9155,13 @@ namespace ts {
91559155
return false;
91569156
}
91579157

9158+
function getInitializedParameterReducedType(declaredType: UnionType, assignedType: Type) {
9159+
if (declaredType === assignedType || getFalsyFlags(assignedType) & TypeFlags.Undefined) {
9160+
return declaredType;
9161+
}
9162+
return getTypeWithFacts(declaredType, TypeFacts.NEUndefined);
9163+
}
9164+
91589165
// Remove those constituent types of declaredType to which no constituent type of assignedType is assignable.
91599166
// For example, when a variable of type number | string | boolean is assigned a value of type number | boolean,
91609167
// we remove type string.
@@ -9351,7 +9358,7 @@ namespace ts {
93519358
getInitialTypeOfBindingElement(<BindingElement>node);
93529359
}
93539360

9354-
function getInitialOrAssignedType(node: VariableDeclaration | BindingElement | Expression) {
9361+
function getInitialOrAssignedType(node: VariableDeclaration | BindingElement | Expression | ParameterDeclaration) {
93559362
return node.kind === SyntaxKind.VariableDeclaration || node.kind === SyntaxKind.BindingElement || node.kind === SyntaxKind.Parameter ?
93569363
getInitialType(<VariableDeclaration | BindingElement | ParameterDeclaration>node) :
93579364
getAssignedType(<Expression>node);
@@ -9622,6 +9629,13 @@ namespace ts {
96229629
continue;
96239630
}
96249631
}
9632+
else if (flow.flags & FlowFlags.InitializedParameter) {
9633+
type = getTypeAtFlowInitializedParameter(flow as FlowInitializedParameter);
9634+
if (!type) {
9635+
flow = (flow as FlowInitializedParameter).antecedent;
9636+
continue;
9637+
}
9638+
}
96259639
else if (flow.flags & FlowFlags.Condition) {
96269640
type = getTypeAtFlowCondition(<FlowCondition>flow);
96279641
}
@@ -9669,6 +9683,20 @@ namespace ts {
96699683
}
96709684
}
96719685

9686+
function getTypeAtFlowInitializedParameter(flow: FlowInitializedParameter) {
9687+
const node = flow.node;
9688+
// Parameter initializers don't really narrow the declared type except to remove undefined.
9689+
// If the initializer includes undefined in the type, it doesn't even remove undefined.
9690+
if (isMatchingReference(reference, node)) {
9691+
if (declaredType.flags & TypeFlags.Union) {
9692+
return getInitializedParameterReducedType(<UnionType>declaredType, getInitialOrAssignedType(node));
9693+
}
9694+
return declaredType;
9695+
}
9696+
9697+
return undefined;
9698+
}
9699+
96729700
function getTypeAtFlowAssignment(flow: FlowAssignment) {
96739701
const node = flow.node;
96749702
// Assignments only narrow the computed type if the declared type is a union type. Thus, we

src/compiler/types.ts

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2042,12 +2042,13 @@ namespace ts {
20422042
BranchLabel = 1 << 2, // Non-looping junction
20432043
LoopLabel = 1 << 3, // Looping junction
20442044
Assignment = 1 << 4, // Assignment
2045-
TrueCondition = 1 << 5, // Condition known to be true
2046-
FalseCondition = 1 << 6, // Condition known to be false
2047-
SwitchClause = 1 << 7, // Switch statement clause
2048-
ArrayMutation = 1 << 8, // Potential array mutation
2049-
Referenced = 1 << 9, // Referenced as antecedent once
2050-
Shared = 1 << 10, // Referenced as antecedent more than once
2045+
InitializedParameter = 1 << 5, // Parameter with initializer
2046+
TrueCondition = 1 << 6, // Condition known to be true
2047+
FalseCondition = 1 << 7, // Condition known to be false
2048+
SwitchClause = 1 << 8, // Switch statement clause
2049+
ArrayMutation = 1 << 9, // Potential array mutation
2050+
Referenced = 1 << 10, // Referenced as antecedent once
2051+
Shared = 1 << 11, // Referenced as antecedent more than once
20512052
Label = BranchLabel | LoopLabel,
20522053
Condition = TrueCondition | FalseCondition
20532054
}
@@ -2097,6 +2098,11 @@ namespace ts {
20972098
antecedent: FlowNode;
20982099
}
20992100

2101+
export interface FlowInitializedParameter extends FlowNode {
2102+
node: ParameterDeclaration;
2103+
antecedent: FlowNode;
2104+
}
2105+
21002106
export type FlowType = Type | IncompleteType;
21012107

21022108
// Incomplete types occur during control flow analysis of loops. An IncompleteType

0 commit comments

Comments
 (0)