Detect circularities when removing 'undefined' from defaulted params (#37023)

Fixes #37008

Note that referencing a variable in its initializer is a TDZ error;
the OP report had OOB logic that prevented this in practice (?)
This commit is contained in:
Ryan Cavanaugh 2020-02-26 14:59:04 -08:00 committed by GitHub
parent f7d2beb3f5
commit c4e96856ac
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 104 additions and 6 deletions

View File

@ -20522,12 +20522,20 @@ namespace ts {
/** 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;
if (pushTypeResolution(declaration.symbol, TypeSystemPropertyName.DeclaredType)) {
const annotationIncludesUndefined = strictNullChecks &&
declaration.kind === SyntaxKind.Parameter &&
declaration.initializer &&
getFalsyFlags(declaredType) & TypeFlags.Undefined &&
!(getFalsyFlags(checkExpression(declaration.initializer)) & TypeFlags.Undefined);
popTypeResolution();
return annotationIncludesUndefined ? getTypeWithFacts(declaredType, TypeFacts.NEUndefined) : declaredType;
}
else {
reportCircularityError(declaration.symbol);
return declaredType;
}
}
function isConstraintPosition(node: Node) {

View File

@ -0,0 +1,32 @@
tests/cases/compiler/circularOptionalityRemoval.ts(2,14): error TS2502: 'x' is referenced directly or indirectly in its own type annotation.
tests/cases/compiler/circularOptionalityRemoval.ts(2,38): error TS2372: Parameter 'x' cannot be referenced in its initializer.
tests/cases/compiler/circularOptionalityRemoval.ts(2,38): error TS2532: Object is possibly 'undefined'.
tests/cases/compiler/circularOptionalityRemoval.ts(2,46): error TS2372: Parameter 'x' cannot be referenced in its initializer.
tests/cases/compiler/circularOptionalityRemoval.ts(5,14): error TS1015: Parameter cannot have question mark and initializer.
tests/cases/compiler/circularOptionalityRemoval.ts(5,14): error TS2502: 'x' is referenced directly or indirectly in its own type annotation.
tests/cases/compiler/circularOptionalityRemoval.ts(5,27): error TS2304: Cannot find name 'someCondition'.
tests/cases/compiler/circularOptionalityRemoval.ts(5,54): error TS2372: Parameter 'x' cannot be referenced in its initializer.
==== tests/cases/compiler/circularOptionalityRemoval.ts (8 errors) ====
// Constructed repro
function fn1(x: number | undefined = x > 0 ? x : 0) { }
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
!!! error TS2502: 'x' is referenced directly or indirectly in its own type annotation.
~
!!! error TS2372: Parameter 'x' cannot be referenced in its initializer.
~
!!! error TS2532: Object is possibly 'undefined'.
~
!!! error TS2372: Parameter 'x' cannot be referenced in its initializer.
// Report from user
function fn2(x?: string = someCondition ? 'value1' : x) { }
~
!!! error TS1015: Parameter cannot have question mark and initializer.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
!!! error TS2502: 'x' is referenced directly or indirectly in its own type annotation.
~~~~~~~~~~~~~
!!! error TS2304: Cannot find name 'someCondition'.
~
!!! error TS2372: Parameter 'x' cannot be referenced in its initializer.

View File

@ -0,0 +1,16 @@
//// [circularOptionalityRemoval.ts]
// Constructed repro
function fn1(x: number | undefined = x > 0 ? x : 0) { }
// Report from user
function fn2(x?: string = someCondition ? 'value1' : x) { }
//// [circularOptionalityRemoval.js]
// Constructed repro
function fn1(x) {
if (x === void 0) { x = x > 0 ? x : 0; }
}
// Report from user
function fn2(x) {
if (x === void 0) { x = someCondition ? 'value1' : x; }
}

View File

@ -0,0 +1,14 @@
=== tests/cases/compiler/circularOptionalityRemoval.ts ===
// Constructed repro
function fn1(x: number | undefined = x > 0 ? x : 0) { }
>fn1 : Symbol(fn1, Decl(circularOptionalityRemoval.ts, 0, 0))
>x : Symbol(x, Decl(circularOptionalityRemoval.ts, 1, 13))
>x : Symbol(x, Decl(circularOptionalityRemoval.ts, 1, 13))
>x : Symbol(x, Decl(circularOptionalityRemoval.ts, 1, 13))
// Report from user
function fn2(x?: string = someCondition ? 'value1' : x) { }
>fn2 : Symbol(fn2, Decl(circularOptionalityRemoval.ts, 1, 55))
>x : Symbol(x, Decl(circularOptionalityRemoval.ts, 4, 13))
>x : Symbol(x, Decl(circularOptionalityRemoval.ts, 4, 13))

View File

@ -0,0 +1,21 @@
=== tests/cases/compiler/circularOptionalityRemoval.ts ===
// Constructed repro
function fn1(x: number | undefined = x > 0 ? x : 0) { }
>fn1 : (x?: number | undefined) => void
>x : number | undefined
>x > 0 ? x : 0 : number | undefined
>x > 0 : boolean
>x : number | undefined
>0 : 0
>x : number | undefined
>0 : 0
// Report from user
function fn2(x?: string = someCondition ? 'value1' : x) { }
>fn2 : (x?: string | undefined) => void
>x : string | undefined
>someCondition ? 'value1' : x : string | undefined
>someCondition : any
>'value1' : "value1"
>x : string | undefined

View File

@ -0,0 +1,7 @@
// @strictNullChecks: true
// Constructed repro
function fn1(x: number | undefined = x > 0 ? x : 0) { }
// Report from user
function fn2(x?: string = someCondition ? 'value1' : x) { }