mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-02-24 04:30:53 -06:00
Merge pull request #14498 from Microsoft/narrow-default-initialised-parameters
Remove undefined from the type of default initialised parameters when narrowing
This commit is contained in:
commit
1bf4f06b2a
@ -3369,16 +3369,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) {
|
||||
@ -3413,7 +3403,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);
|
||||
}
|
||||
|
||||
@ -10265,14 +10255,11 @@ namespace ts {
|
||||
return false;
|
||||
}
|
||||
|
||||
function getFlowTypeOfReference(reference: Node, declaredType: Type, assumeInitialized: boolean, flowContainer: Node) {
|
||||
function getFlowTypeOfReference(reference: Node, declaredType: Type, initialType = declaredType, flowContainer?: Node, couldBeUninitialized?: boolean) {
|
||||
let key: string;
|
||||
if (!reference.flowNode || assumeInitialized && !(declaredType.flags & TypeFlags.Narrowable)) {
|
||||
if (!reference.flowNode || !couldBeUninitialized && !(declaredType.flags & TypeFlags.Narrowable)) {
|
||||
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;
|
||||
@ -10951,6 +10938,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 &&
|
||||
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) {
|
||||
@ -11069,7 +11066,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) :
|
||||
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.
|
||||
@ -11335,7 +11335,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)) {
|
||||
@ -13327,7 +13327,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;
|
||||
}
|
||||
|
||||
|
||||
@ -18,12 +18,21 @@ function foo2(x = "string", b: number) {
|
||||
|
||||
function foo3(x: string | undefined = "string", b: number) {
|
||||
x.length; // ok, should be string
|
||||
x = undefined;
|
||||
}
|
||||
|
||||
function foo4(x: string | undefined = undefined, b: number) {
|
||||
x; // should be string | undefined
|
||||
x = undefined;
|
||||
}
|
||||
|
||||
type OptionalNullableString = string | null | undefined;
|
||||
function allowsNull(val: OptionalNullableString = "") {
|
||||
val = null;
|
||||
val = 'string and null are both ok';
|
||||
}
|
||||
allowsNull(null); // still allows passing null
|
||||
|
||||
|
||||
|
||||
// .d.ts should have `string | undefined` for foo1, foo2, foo3 and foo4
|
||||
@ -72,11 +81,19 @@ function foo2(x, b) {
|
||||
function foo3(x, b) {
|
||||
if (x === void 0) { x = "string"; }
|
||||
x.length; // ok, should be string
|
||||
x = undefined;
|
||||
}
|
||||
function foo4(x, b) {
|
||||
if (x === void 0) { x = undefined; }
|
||||
x; // should be string | undefined
|
||||
x = undefined;
|
||||
}
|
||||
function allowsNull(val) {
|
||||
if (val === void 0) { val = ""; }
|
||||
val = null;
|
||||
val = 'string and null are both ok';
|
||||
}
|
||||
allowsNull(null); // still allows passing null
|
||||
// .d.ts should have `string | undefined` for foo1, foo2, foo3 and foo4
|
||||
foo1(undefined, 1);
|
||||
foo2(undefined, 1);
|
||||
@ -107,6 +124,8 @@ declare function foo1(x: string | undefined, b: number): void;
|
||||
declare function foo2(x: string | undefined, b: number): void;
|
||||
declare function foo3(x: string | undefined, b: number): void;
|
||||
declare function foo4(x: string | undefined, b: number): void;
|
||||
declare type OptionalNullableString = string | null | undefined;
|
||||
declare function allowsNull(val?: OptionalNullableString): void;
|
||||
declare function removeUndefinedButNotFalse(x?: boolean): false | undefined;
|
||||
declare const cond: boolean;
|
||||
declare function removeNothing(y?: boolean | undefined): boolean;
|
||||
|
||||
@ -66,18 +66,43 @@ function foo3(x: string | undefined = "string", b: number) {
|
||||
>x.length : Symbol(String.length, Decl(lib.d.ts, --, --))
|
||||
>x : Symbol(x, Decl(defaultParameterAddsUndefinedWithStrictNullChecks.ts, 17, 14))
|
||||
>length : Symbol(String.length, Decl(lib.d.ts, --, --))
|
||||
|
||||
x = undefined;
|
||||
>x : Symbol(x, Decl(defaultParameterAddsUndefinedWithStrictNullChecks.ts, 17, 14))
|
||||
>undefined : Symbol(undefined)
|
||||
}
|
||||
|
||||
function foo4(x: string | undefined = undefined, b: number) {
|
||||
>foo4 : Symbol(foo4, Decl(defaultParameterAddsUndefinedWithStrictNullChecks.ts, 19, 1))
|
||||
>x : Symbol(x, Decl(defaultParameterAddsUndefinedWithStrictNullChecks.ts, 21, 14))
|
||||
>foo4 : Symbol(foo4, Decl(defaultParameterAddsUndefinedWithStrictNullChecks.ts, 20, 1))
|
||||
>x : Symbol(x, Decl(defaultParameterAddsUndefinedWithStrictNullChecks.ts, 22, 14))
|
||||
>undefined : Symbol(undefined)
|
||||
>b : Symbol(b, Decl(defaultParameterAddsUndefinedWithStrictNullChecks.ts, 21, 48))
|
||||
>b : Symbol(b, Decl(defaultParameterAddsUndefinedWithStrictNullChecks.ts, 22, 48))
|
||||
|
||||
x; // should be string | undefined
|
||||
>x : Symbol(x, Decl(defaultParameterAddsUndefinedWithStrictNullChecks.ts, 21, 14))
|
||||
>x : Symbol(x, Decl(defaultParameterAddsUndefinedWithStrictNullChecks.ts, 22, 14))
|
||||
|
||||
x = undefined;
|
||||
>x : Symbol(x, Decl(defaultParameterAddsUndefinedWithStrictNullChecks.ts, 22, 14))
|
||||
>undefined : Symbol(undefined)
|
||||
}
|
||||
|
||||
type OptionalNullableString = string | null | undefined;
|
||||
>OptionalNullableString : Symbol(OptionalNullableString, Decl(defaultParameterAddsUndefinedWithStrictNullChecks.ts, 25, 1))
|
||||
|
||||
function allowsNull(val: OptionalNullableString = "") {
|
||||
>allowsNull : Symbol(allowsNull, Decl(defaultParameterAddsUndefinedWithStrictNullChecks.ts, 27, 56))
|
||||
>val : Symbol(val, Decl(defaultParameterAddsUndefinedWithStrictNullChecks.ts, 28, 20))
|
||||
>OptionalNullableString : Symbol(OptionalNullableString, Decl(defaultParameterAddsUndefinedWithStrictNullChecks.ts, 25, 1))
|
||||
|
||||
val = null;
|
||||
>val : Symbol(val, Decl(defaultParameterAddsUndefinedWithStrictNullChecks.ts, 28, 20))
|
||||
|
||||
val = 'string and null are both ok';
|
||||
>val : Symbol(val, Decl(defaultParameterAddsUndefinedWithStrictNullChecks.ts, 28, 20))
|
||||
}
|
||||
allowsNull(null); // still allows passing null
|
||||
>allowsNull : Symbol(allowsNull, Decl(defaultParameterAddsUndefinedWithStrictNullChecks.ts, 27, 56))
|
||||
|
||||
|
||||
|
||||
// .d.ts should have `string | undefined` for foo1, foo2, foo3 and foo4
|
||||
@ -94,40 +119,40 @@ foo3(undefined, 1);
|
||||
>undefined : Symbol(undefined)
|
||||
|
||||
foo4(undefined, 1);
|
||||
>foo4 : Symbol(foo4, Decl(defaultParameterAddsUndefinedWithStrictNullChecks.ts, 19, 1))
|
||||
>foo4 : Symbol(foo4, Decl(defaultParameterAddsUndefinedWithStrictNullChecks.ts, 20, 1))
|
||||
>undefined : Symbol(undefined)
|
||||
|
||||
|
||||
function removeUndefinedButNotFalse(x = true) {
|
||||
>removeUndefinedButNotFalse : Symbol(removeUndefinedButNotFalse, Decl(defaultParameterAddsUndefinedWithStrictNullChecks.ts, 31, 19))
|
||||
>x : Symbol(x, Decl(defaultParameterAddsUndefinedWithStrictNullChecks.ts, 34, 36))
|
||||
>removeUndefinedButNotFalse : Symbol(removeUndefinedButNotFalse, Decl(defaultParameterAddsUndefinedWithStrictNullChecks.ts, 40, 19))
|
||||
>x : Symbol(x, Decl(defaultParameterAddsUndefinedWithStrictNullChecks.ts, 43, 36))
|
||||
|
||||
if (x === false) {
|
||||
>x : Symbol(x, Decl(defaultParameterAddsUndefinedWithStrictNullChecks.ts, 34, 36))
|
||||
>x : Symbol(x, Decl(defaultParameterAddsUndefinedWithStrictNullChecks.ts, 43, 36))
|
||||
|
||||
return x;
|
||||
>x : Symbol(x, Decl(defaultParameterAddsUndefinedWithStrictNullChecks.ts, 34, 36))
|
||||
>x : Symbol(x, Decl(defaultParameterAddsUndefinedWithStrictNullChecks.ts, 43, 36))
|
||||
}
|
||||
}
|
||||
|
||||
declare const cond: boolean;
|
||||
>cond : Symbol(cond, Decl(defaultParameterAddsUndefinedWithStrictNullChecks.ts, 40, 13))
|
||||
>cond : Symbol(cond, Decl(defaultParameterAddsUndefinedWithStrictNullChecks.ts, 49, 13))
|
||||
|
||||
function removeNothing(y = cond ? true : undefined) {
|
||||
>removeNothing : Symbol(removeNothing, Decl(defaultParameterAddsUndefinedWithStrictNullChecks.ts, 40, 28))
|
||||
>y : Symbol(y, Decl(defaultParameterAddsUndefinedWithStrictNullChecks.ts, 41, 23))
|
||||
>cond : Symbol(cond, Decl(defaultParameterAddsUndefinedWithStrictNullChecks.ts, 40, 13))
|
||||
>removeNothing : Symbol(removeNothing, Decl(defaultParameterAddsUndefinedWithStrictNullChecks.ts, 49, 28))
|
||||
>y : Symbol(y, Decl(defaultParameterAddsUndefinedWithStrictNullChecks.ts, 50, 23))
|
||||
>cond : Symbol(cond, Decl(defaultParameterAddsUndefinedWithStrictNullChecks.ts, 49, 13))
|
||||
>undefined : Symbol(undefined)
|
||||
|
||||
if (y !== undefined) {
|
||||
>y : Symbol(y, Decl(defaultParameterAddsUndefinedWithStrictNullChecks.ts, 41, 23))
|
||||
>y : Symbol(y, Decl(defaultParameterAddsUndefinedWithStrictNullChecks.ts, 50, 23))
|
||||
>undefined : Symbol(undefined)
|
||||
|
||||
if (y === false) {
|
||||
>y : Symbol(y, Decl(defaultParameterAddsUndefinedWithStrictNullChecks.ts, 41, 23))
|
||||
>y : Symbol(y, Decl(defaultParameterAddsUndefinedWithStrictNullChecks.ts, 50, 23))
|
||||
|
||||
return y;
|
||||
>y : Symbol(y, Decl(defaultParameterAddsUndefinedWithStrictNullChecks.ts, 41, 23))
|
||||
>y : Symbol(y, Decl(defaultParameterAddsUndefinedWithStrictNullChecks.ts, 50, 23))
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
||||
@ -86,7 +86,7 @@ function foo2(x = "string", b: number) {
|
||||
|
||||
function foo3(x: string | undefined = "string", b: number) {
|
||||
>foo3 : (x: string | undefined, b: number) => void
|
||||
>x : string
|
||||
>x : string | undefined
|
||||
>"string" : "string"
|
||||
>b : number
|
||||
|
||||
@ -94,6 +94,11 @@ function foo3(x: string | undefined = "string", b: number) {
|
||||
>x.length : number
|
||||
>x : string
|
||||
>length : number
|
||||
|
||||
x = undefined;
|
||||
>x = undefined : undefined
|
||||
>x : string | undefined
|
||||
>undefined : undefined
|
||||
}
|
||||
|
||||
function foo4(x: string | undefined = undefined, b: number) {
|
||||
@ -104,8 +109,38 @@ function foo4(x: string | undefined = undefined, b: number) {
|
||||
|
||||
x; // should be string | undefined
|
||||
>x : string | undefined
|
||||
|
||||
x = undefined;
|
||||
>x = undefined : undefined
|
||||
>x : string | undefined
|
||||
>undefined : undefined
|
||||
}
|
||||
|
||||
type OptionalNullableString = string | null | undefined;
|
||||
>OptionalNullableString : OptionalNullableString
|
||||
>null : null
|
||||
|
||||
function allowsNull(val: OptionalNullableString = "") {
|
||||
>allowsNull : (val?: OptionalNullableString) => void
|
||||
>val : OptionalNullableString
|
||||
>OptionalNullableString : OptionalNullableString
|
||||
>"" : ""
|
||||
|
||||
val = null;
|
||||
>val = null : null
|
||||
>val : OptionalNullableString
|
||||
>null : null
|
||||
|
||||
val = 'string and null are both ok';
|
||||
>val = 'string and null are both ok' : "string and null are both ok"
|
||||
>val : OptionalNullableString
|
||||
>'string and null are both ok' : "string and null are both ok"
|
||||
}
|
||||
allowsNull(null); // still allows passing null
|
||||
>allowsNull(null) : void
|
||||
>allowsNull : (val?: OptionalNullableString) => void
|
||||
>null : null
|
||||
|
||||
|
||||
|
||||
// .d.ts should have `string | undefined` for foo1, foo2, foo3 and foo4
|
||||
|
||||
@ -19,12 +19,21 @@ function foo2(x = "string", b: number) {
|
||||
|
||||
function foo3(x: string | undefined = "string", b: number) {
|
||||
x.length; // ok, should be string
|
||||
x = undefined;
|
||||
}
|
||||
|
||||
function foo4(x: string | undefined = undefined, b: number) {
|
||||
x; // should be string | undefined
|
||||
x = undefined;
|
||||
}
|
||||
|
||||
type OptionalNullableString = string | null | undefined;
|
||||
function allowsNull(val: OptionalNullableString = "") {
|
||||
val = null;
|
||||
val = 'string and null are both ok';
|
||||
}
|
||||
allowsNull(null); // still allows passing null
|
||||
|
||||
|
||||
|
||||
// .d.ts should have `string | undefined` for foo1, foo2, foo3 and foo4
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user