-
Notifications
You must be signed in to change notification settings - Fork 12.9k
Remove undefined from the type of default initialised parameters when narrowing #14498
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 5 commits
eaca169
533ce82
36513f2
2325fda
c1f4c9c
24c8de2
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3368,16 +3368,6 @@ namespace ts { | |
return strictNullChecks && optional ? includeFalsyTypes(type, TypeFlags.Undefined) : type; | ||
} | ||
|
||
/** remove undefined from the annotated type of a parameter when there is an initializer (that doesn't include undefined) */ | ||
function removeOptionalityFromAnnotation(annotatedType: Type, declaration: VariableLikeDeclaration): Type { | ||
const annotationIncludesUndefined = strictNullChecks && | ||
declaration.kind === SyntaxKind.Parameter && | ||
declaration.initializer && | ||
getFalsyFlags(annotatedType) & TypeFlags.Undefined && | ||
!(getFalsyFlags(checkExpression(declaration.initializer)) & TypeFlags.Undefined); | ||
return annotationIncludesUndefined ? getNonNullableType(annotatedType) : annotatedType; | ||
} | ||
|
||
// Return the inferred type for a variable, parameter, or property declaration | ||
function getTypeForVariableLikeDeclaration(declaration: VariableLikeDeclaration, includeOptionality: boolean): Type { | ||
if (declaration.flags & NodeFlags.JavaScriptFile) { | ||
|
@@ -3412,7 +3402,7 @@ namespace ts { | |
|
||
// Use type from type annotation if one is present | ||
if (declaration.type) { | ||
const declaredType = removeOptionalityFromAnnotation(getTypeFromTypeNode(declaration.type), declaration); | ||
const declaredType = getTypeFromTypeNode(declaration.type); | ||
return addOptionality(declaredType, /*optional*/ declaration.questionToken && includeOptionality); | ||
} | ||
|
||
|
@@ -10248,14 +10238,15 @@ namespace ts { | |
return false; | ||
} | ||
|
||
function getFlowTypeOfReference(reference: Node, declaredType: Type, assumeInitialized: boolean, flowContainer: Node) { | ||
function isFlowNarrowable(reference: Node, type: Type, couldBeUninitialized?: boolean) { | ||
return reference.flowNode && (type.flags & TypeFlags.Narrowable || couldBeUninitialized); | ||
} | ||
|
||
function getFlowTypeOfReference(reference: Node, declaredType: Type, initialType = declaredType, flowContainer?: Node, couldBeUninitialized?: boolean) { | ||
let key: string; | ||
if (!reference.flowNode || assumeInitialized && !(declaredType.flags & TypeFlags.Narrowable)) { | ||
if (!isFlowNarrowable(reference, declaredType, couldBeUninitialized)) { | ||
return declaredType; | ||
} | ||
const initialType = assumeInitialized ? declaredType : | ||
declaredType === autoType || declaredType === autoArrayType ? undefinedType : | ||
includeFalsyTypes(declaredType, TypeFlags.Undefined); | ||
const visitedFlowStart = visitedFlowCount; | ||
const evolvedType = getTypeFromFlowType(getTypeAtFlowNode(reference.flowNode)); | ||
visitedFlowCount = visitedFlowStart; | ||
|
@@ -10934,6 +10925,16 @@ namespace ts { | |
return symbol.flags & SymbolFlags.Variable && (getDeclarationNodeFlagsFromSymbol(symbol) & NodeFlags.Const) !== 0 && getTypeOfSymbol(symbol) !== autoArrayType; | ||
} | ||
|
||
/** remove undefined from the annotated type of a parameter when there is an initializer (that doesn't include undefined) */ | ||
function removeOptionalityFromDeclaredType(declaredType: Type, declaration: VariableLikeDeclaration): Type { | ||
const annotationIncludesUndefined = strictNullChecks && | ||
declaration.kind === SyntaxKind.Parameter && | ||
declaration.initializer && | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This line is the source if the issue with There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is the cause of #14236. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done |
||
getFalsyFlags(declaredType) & TypeFlags.Undefined && | ||
!(getFalsyFlags(checkExpression(declaration.initializer)) & TypeFlags.Undefined); | ||
return annotationIncludesUndefined ? getTypeWithFacts(declaredType, TypeFacts.NEUndefined) : declaredType; | ||
} | ||
|
||
function checkIdentifier(node: Identifier): Type { | ||
const symbol = getResolvedSymbol(node); | ||
if (symbol === unknownSymbol) { | ||
|
@@ -11052,7 +11053,10 @@ namespace ts { | |
const assumeInitialized = isParameter || isOuterVariable || | ||
type !== autoType && type !== autoArrayType && (!strictNullChecks || (type.flags & TypeFlags.Any) !== 0 || isInTypeQuery(node)) || | ||
isInAmbientContext(declaration); | ||
const flowType = getFlowTypeOfReference(node, type, assumeInitialized, flowContainer); | ||
const initialType = assumeInitialized ? (isParameter ? removeOptionalityFromDeclaredType(type, getRootDeclaration(declaration) as VariableLikeDeclaration) : type) : | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would keep the |
||
type === autoType || type === autoArrayType ? undefinedType : | ||
includeFalsyTypes(type, TypeFlags.Undefined); | ||
const flowType = getFlowTypeOfReference(node, type, initialType, flowContainer, !assumeInitialized); | ||
// A variable is considered uninitialized when it is possible to analyze the entire control flow graph | ||
// from declaration to use, and when the variable's declared type doesn't include undefined but the | ||
// control flow based type does include undefined. | ||
|
@@ -11318,7 +11322,7 @@ namespace ts { | |
if (isClassLike(container.parent)) { | ||
const symbol = getSymbolOfNode(container.parent); | ||
const type = hasModifier(container, ModifierFlags.Static) ? getTypeOfSymbol(symbol) : (<InterfaceType>getDeclaredTypeOfSymbol(symbol)).thisType; | ||
return getFlowTypeOfReference(node, type, /*assumeInitialized*/ true, /*flowContainer*/ undefined); | ||
return getFlowTypeOfReference(node, type); | ||
} | ||
|
||
if (isInJavaScriptFile(node)) { | ||
|
@@ -13309,7 +13313,7 @@ namespace ts { | |
!(prop.flags & SymbolFlags.Method && propType.flags & TypeFlags.Union)) { | ||
return propType; | ||
} | ||
const flowType = getFlowTypeOfReference(node, propType, /*assumeInitialized*/ true, /*flowContainer*/ undefined); | ||
const flowType = getFlowTypeOfReference(node, propType); | ||
return assignmentKind ? getBaseTypeOfLiteralType(flowType) : flowType; | ||
} | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just inline the function here. It's not called anywhere else best I can tell.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done