Skip to content

Commit

Permalink
Detect circularities when removing 'undefined' from defaulted params (#…
Browse files Browse the repository at this point in the history
…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 (?)
  • Loading branch information
RyanCavanaugh authored Feb 26, 2020
1 parent f7d2beb commit c4e9685
Show file tree
Hide file tree
Showing 6 changed files with 104 additions and 6 deletions.
20 changes: 14 additions & 6 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
32 changes: 32 additions & 0 deletions tests/baselines/reference/circularOptionalityRemoval.errors.txt
Original file line number Diff line number Diff line change
@@ -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.
16 changes: 16 additions & 0 deletions tests/baselines/reference/circularOptionalityRemoval.js
Original file line number Diff line number Diff line change
@@ -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; }
}
14 changes: 14 additions & 0 deletions tests/baselines/reference/circularOptionalityRemoval.symbols
Original file line number Diff line number Diff line change
@@ -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))

21 changes: 21 additions & 0 deletions tests/baselines/reference/circularOptionalityRemoval.types
Original file line number Diff line number Diff line change
@@ -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

7 changes: 7 additions & 0 deletions tests/cases/compiler/circularOptionalityRemoval.ts
Original file line number Diff line number Diff line change
@@ -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) { }

0 comments on commit c4e9685

Please sign in to comment.