Skip to content

Commit eaca169

Browse files
committed
Remove optionality from the initial type of default-valued parameters
When narrowing, remove optionality from the initial type of parameters with initialisers. Note that the type of the initialiser is not used; its presence just means that the initial type of the parameter can't contain undefined. It could contain any other member of a declared union type.
1 parent 0425175 commit eaca169

File tree

1 file changed

+22
-21
lines changed

1 file changed

+22
-21
lines changed

src/compiler/checker.ts

Lines changed: 22 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -3368,16 +3368,6 @@ namespace ts {
33683368
return strictNullChecks && optional ? includeFalsyTypes(type, TypeFlags.Undefined) : type;
33693369
}
33703370

3371-
/** remove undefined from the annotated type of a parameter when there is an initializer (that doesn't include undefined) */
3372-
function removeOptionalityFromAnnotation(annotatedType: Type, declaration: VariableLikeDeclaration): Type {
3373-
const annotationIncludesUndefined = strictNullChecks &&
3374-
declaration.kind === SyntaxKind.Parameter &&
3375-
declaration.initializer &&
3376-
getFalsyFlags(annotatedType) & TypeFlags.Undefined &&
3377-
!(getFalsyFlags(checkExpression(declaration.initializer)) & TypeFlags.Undefined);
3378-
return annotationIncludesUndefined ? getNonNullableType(annotatedType) : annotatedType;
3379-
}
3380-
33813371
// Return the inferred type for a variable, parameter, or property declaration
33823372
function getTypeForVariableLikeDeclaration(declaration: VariableLikeDeclaration, includeOptionality: boolean): Type {
33833373
if (declaration.flags & NodeFlags.JavaScriptFile) {
@@ -3412,7 +3402,7 @@ namespace ts {
34123402

34133403
// Use type from type annotation if one is present
34143404
if (declaration.type) {
3415-
const declaredType = removeOptionalityFromAnnotation(getTypeFromTypeNode(declaration.type), declaration);
3405+
const declaredType = getTypeFromTypeNode(declaration.type);
34163406
return addOptionality(declaredType, /*optional*/ declaration.questionToken && includeOptionality);
34173407
}
34183408

@@ -10248,14 +10238,12 @@ namespace ts {
1024810238
return false;
1024910239
}
1025010240

10251-
function getFlowTypeOfReference(reference: Node, declaredType: Type, assumeInitialized: boolean, flowContainer: Node) {
10241+
function isFlowNarrowable(reference: Node, type: Type, couldBeUninitialized?: boolean) {
10242+
return reference.flowNode && (type.flags & TypeFlags.Narrowable || couldBeUninitialized);
10243+
}
10244+
10245+
function getFlowTypeOfReference(reference: Node, declaredType: Type, initialType = declaredType, flowContainer?: Node) {
1025210246
let key: string;
10253-
if (!reference.flowNode || assumeInitialized && !(declaredType.flags & TypeFlags.Narrowable)) {
10254-
return declaredType;
10255-
}
10256-
const initialType = assumeInitialized ? declaredType :
10257-
declaredType === autoType || declaredType === autoArrayType ? undefinedType :
10258-
includeFalsyTypes(declaredType, TypeFlags.Undefined);
1025910247
const visitedFlowStart = visitedFlowCount;
1026010248
const evolvedType = getTypeFromFlowType(getTypeAtFlowNode(reference.flowNode));
1026110249
visitedFlowCount = visitedFlowStart;
@@ -10934,6 +10922,16 @@ namespace ts {
1093410922
return symbol.flags & SymbolFlags.Variable && (getDeclarationNodeFlagsFromSymbol(symbol) & NodeFlags.Const) !== 0 && getTypeOfSymbol(symbol) !== autoArrayType;
1093510923
}
1093610924

10925+
/** remove undefined from the annotated type of a parameter when there is an initializer (that doesn't include undefined) */
10926+
function removeOptionalityFromDeclaredType(declaredType: Type, declaration: VariableLikeDeclaration): Type {
10927+
const annotationIncludesUndefined = strictNullChecks &&
10928+
declaration.kind === SyntaxKind.Parameter &&
10929+
declaration.initializer &&
10930+
getFalsyFlags(declaredType) & TypeFlags.Undefined &&
10931+
!(getFalsyFlags(checkExpression(declaration.initializer)) & TypeFlags.Undefined);
10932+
return annotationIncludesUndefined ? getNonNullableType(declaredType) : declaredType;
10933+
}
10934+
1093710935
function checkIdentifier(node: Identifier): Type {
1093810936
const symbol = getResolvedSymbol(node);
1093910937
if (symbol === unknownSymbol) {
@@ -11052,7 +11050,10 @@ namespace ts {
1105211050
const assumeInitialized = isParameter || isOuterVariable ||
1105311051
type !== autoType && type !== autoArrayType && (!strictNullChecks || (type.flags & TypeFlags.Any) !== 0 || isInTypeQuery(node)) ||
1105411052
isInAmbientContext(declaration);
11055-
const flowType = getFlowTypeOfReference(node, type, assumeInitialized, flowContainer);
11053+
const initialType = assumeInitialized ? (isParameter ? removeOptionalityFromDeclaredType(type, getRootDeclaration(declaration) as VariableLikeDeclaration) : type) :
11054+
type === autoType || type === autoArrayType ? undefinedType :
11055+
includeFalsyTypes(type, TypeFlags.Undefined);
11056+
const flowType = isFlowNarrowable(node, type, !assumeInitialized) ? getFlowTypeOfReference(node, type, initialType, flowContainer) : type;
1105611057
// A variable is considered uninitialized when it is possible to analyze the entire control flow graph
1105711058
// from declaration to use, and when the variable's declared type doesn't include undefined but the
1105811059
// control flow based type does include undefined.
@@ -11318,7 +11319,7 @@ namespace ts {
1131811319
if (isClassLike(container.parent)) {
1131911320
const symbol = getSymbolOfNode(container.parent);
1132011321
const type = hasModifier(container, ModifierFlags.Static) ? getTypeOfSymbol(symbol) : (<InterfaceType>getDeclaredTypeOfSymbol(symbol)).thisType;
11321-
return getFlowTypeOfReference(node, type, /*assumeInitialized*/ true, /*flowContainer*/ undefined);
11322+
return isFlowNarrowable(node, type) ? getFlowTypeOfReference(node, type) : type;
1132211323
}
1132311324

1132411325
if (isInJavaScriptFile(node)) {
@@ -13309,7 +13310,7 @@ namespace ts {
1330913310
!(prop.flags & SymbolFlags.Method && propType.flags & TypeFlags.Union)) {
1331013311
return propType;
1331113312
}
13312-
const flowType = getFlowTypeOfReference(node, propType, /*assumeInitialized*/ true, /*flowContainer*/ undefined);
13313+
const flowType = isFlowNarrowable(node, propType) ? getFlowTypeOfReference(node, propType) : propType;
1331313314
return assignmentKind ? getBaseTypeOfLiteralType(flowType) : flowType;
1331413315
}
1331513316

0 commit comments

Comments
 (0)