diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 34e8a6336ae3c..21d485c002382 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -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) { diff --git a/tests/baselines/reference/circularOptionalityRemoval.errors.txt b/tests/baselines/reference/circularOptionalityRemoval.errors.txt new file mode 100644 index 0000000000000..2b60b5b66213a --- /dev/null +++ b/tests/baselines/reference/circularOptionalityRemoval.errors.txt @@ -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. \ No newline at end of file diff --git a/tests/baselines/reference/circularOptionalityRemoval.js b/tests/baselines/reference/circularOptionalityRemoval.js new file mode 100644 index 0000000000000..c584c2f3949f8 --- /dev/null +++ b/tests/baselines/reference/circularOptionalityRemoval.js @@ -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; } +} diff --git a/tests/baselines/reference/circularOptionalityRemoval.symbols b/tests/baselines/reference/circularOptionalityRemoval.symbols new file mode 100644 index 0000000000000..06bfd9da771a6 --- /dev/null +++ b/tests/baselines/reference/circularOptionalityRemoval.symbols @@ -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)) + diff --git a/tests/baselines/reference/circularOptionalityRemoval.types b/tests/baselines/reference/circularOptionalityRemoval.types new file mode 100644 index 0000000000000..5783b24a6c379 --- /dev/null +++ b/tests/baselines/reference/circularOptionalityRemoval.types @@ -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 + diff --git a/tests/cases/compiler/circularOptionalityRemoval.ts b/tests/cases/compiler/circularOptionalityRemoval.ts new file mode 100644 index 0000000000000..20a6a4085703a --- /dev/null +++ b/tests/cases/compiler/circularOptionalityRemoval.ts @@ -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) { } \ No newline at end of file