diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 0567712f11da3..0f8959d60b4a2 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -29475,7 +29475,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { if (isIdentifier(expr)) { const symbol = getResolvedSymbol(expr); const declaration = getExportSymbolOfValueSymbolIfExported(symbol).valueDeclaration; - if (declaration && (isBindingElement(declaration) || isParameter(declaration)) && reference === declaration.parent && !declaration.initializer && !declaration.dotDotDotToken) { + if (declaration && (isBindingElement(declaration) || isParameter(declaration)) && reference === declaration.parent && !declaration.dotDotDotToken) { return declaration; } } @@ -30947,7 +30947,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { // the binding pattern AST instance for '{ kind, payload }' as a pseudo-reference and narrow this reference // as if it occurred in the specified location. We then recompute the narrowed binding element type by // destructuring from the narrowed parent type. - if (isBindingElement(declaration) && !declaration.initializer && !declaration.dotDotDotToken && declaration.parent.elements.length >= 2) { + if (isBindingElement(declaration) && !declaration.dotDotDotToken && declaration.parent.elements.length >= 2) { const parent = declaration.parent.parent; const rootDeclaration = getRootDeclaration(parent); if (rootDeclaration.kind === SyntaxKind.VariableDeclaration && getCombinedNodeFlagsCached(rootDeclaration) & NodeFlags.Constant || rootDeclaration.kind === SyntaxKind.Parameter) { diff --git a/tests/baselines/reference/discriminatedUnionDestructuringWithDefaults.errors.txt b/tests/baselines/reference/discriminatedUnionDestructuringWithDefaults.errors.txt new file mode 100644 index 0000000000000..09dc8fbf4739e --- /dev/null +++ b/tests/baselines/reference/discriminatedUnionDestructuringWithDefaults.errors.txt @@ -0,0 +1,40 @@ +discriminatedUnionDestructuringWithDefaults.ts(26,43): error TS2322: Type 'boolean' is not assignable to type 'string | number'. + + +==== discriminatedUnionDestructuringWithDefaults.ts (1 errors) ==== + // Case 1: the issue #50139 repro — must compile clean after the fix + function repro({ isText = false, children = 0 }: + { isText: true; children?: string } + | { isText: false; children?: number } + ) { + if (isText === true) { + let data: string = children; + } else if (isText === false) { + let data: number = children; + } + } + + // Case 2: control — no defaults, already worked, must not regress + function control({ isText, children }: + { isText: true; children: string } + | { isText: false; children: number } + ) { + if (isText === true) { + let data: string = children; + } else if (isText === false) { + let data: number = children; + } + } + + // Case 3: whzx5byb's soundness check — default foreign to one arm must still error + function soundnessCheck({ isText = false, children = true }: // expect error on `children = true` + ~~~~~~~~ +!!! error TS2322: Type 'boolean' is not assignable to type 'string | number'. + { isText: true; children?: string } + | { isText: false; children?: number } + ) { + if (isText === true) { + let data: string = children; + } + } + \ No newline at end of file diff --git a/tests/baselines/reference/discriminatedUnionDestructuringWithDefaults.js b/tests/baselines/reference/discriminatedUnionDestructuringWithDefaults.js new file mode 100644 index 0000000000000..edf20cf5ba059 --- /dev/null +++ b/tests/baselines/reference/discriminatedUnionDestructuringWithDefaults.js @@ -0,0 +1,64 @@ +//// [tests/cases/compiler/discriminatedUnionDestructuringWithDefaults.ts] //// + +//// [discriminatedUnionDestructuringWithDefaults.ts] +// Case 1: the issue #50139 repro — must compile clean after the fix +function repro({ isText = false, children = 0 }: + { isText: true; children?: string } + | { isText: false; children?: number } +) { + if (isText === true) { + let data: string = children; + } else if (isText === false) { + let data: number = children; + } +} + +// Case 2: control — no defaults, already worked, must not regress +function control({ isText, children }: + { isText: true; children: string } + | { isText: false; children: number } +) { + if (isText === true) { + let data: string = children; + } else if (isText === false) { + let data: number = children; + } +} + +// Case 3: whzx5byb's soundness check — default foreign to one arm must still error +function soundnessCheck({ isText = false, children = true }: // expect error on `children = true` + { isText: true; children?: string } + | { isText: false; children?: number } +) { + if (isText === true) { + let data: string = children; + } +} + + +//// [discriminatedUnionDestructuringWithDefaults.js] +"use strict"; +// Case 1: the issue #50139 repro — must compile clean after the fix +function repro({ isText = false, children = 0 }) { + if (isText === true) { + let data = children; + } + else if (isText === false) { + let data = children; + } +} +// Case 2: control — no defaults, already worked, must not regress +function control({ isText, children }) { + if (isText === true) { + let data = children; + } + else if (isText === false) { + let data = children; + } +} +// Case 3: whzx5byb's soundness check — default foreign to one arm must still error +function soundnessCheck({ isText = false, children = true }) { + if (isText === true) { + let data = children; + } +} diff --git a/tests/baselines/reference/discriminatedUnionDestructuringWithDefaults.symbols b/tests/baselines/reference/discriminatedUnionDestructuringWithDefaults.symbols new file mode 100644 index 0000000000000..f8dd304a7b838 --- /dev/null +++ b/tests/baselines/reference/discriminatedUnionDestructuringWithDefaults.symbols @@ -0,0 +1,89 @@ +//// [tests/cases/compiler/discriminatedUnionDestructuringWithDefaults.ts] //// + +=== discriminatedUnionDestructuringWithDefaults.ts === +// Case 1: the issue #50139 repro — must compile clean after the fix +function repro({ isText = false, children = 0 }: +>repro : Symbol(repro, Decl(discriminatedUnionDestructuringWithDefaults.ts, 0, 0)) +>isText : Symbol(isText, Decl(discriminatedUnionDestructuringWithDefaults.ts, 1, 16)) +>children : Symbol(children, Decl(discriminatedUnionDestructuringWithDefaults.ts, 1, 32)) + + { isText: true; children?: string } +>isText : Symbol(isText, Decl(discriminatedUnionDestructuringWithDefaults.ts, 2, 5)) +>children : Symbol(children, Decl(discriminatedUnionDestructuringWithDefaults.ts, 2, 19)) + + | { isText: false; children?: number } +>isText : Symbol(isText, Decl(discriminatedUnionDestructuringWithDefaults.ts, 3, 5)) +>children : Symbol(children, Decl(discriminatedUnionDestructuringWithDefaults.ts, 3, 20)) + +) { + if (isText === true) { +>isText : Symbol(isText, Decl(discriminatedUnionDestructuringWithDefaults.ts, 1, 16)) + + let data: string = children; +>data : Symbol(data, Decl(discriminatedUnionDestructuringWithDefaults.ts, 6, 11)) +>children : Symbol(children, Decl(discriminatedUnionDestructuringWithDefaults.ts, 1, 32)) + + } else if (isText === false) { +>isText : Symbol(isText, Decl(discriminatedUnionDestructuringWithDefaults.ts, 1, 16)) + + let data: number = children; +>data : Symbol(data, Decl(discriminatedUnionDestructuringWithDefaults.ts, 8, 11)) +>children : Symbol(children, Decl(discriminatedUnionDestructuringWithDefaults.ts, 1, 32)) + } +} + +// Case 2: control — no defaults, already worked, must not regress +function control({ isText, children }: +>control : Symbol(control, Decl(discriminatedUnionDestructuringWithDefaults.ts, 10, 1)) +>isText : Symbol(isText, Decl(discriminatedUnionDestructuringWithDefaults.ts, 13, 18)) +>children : Symbol(children, Decl(discriminatedUnionDestructuringWithDefaults.ts, 13, 26)) + + { isText: true; children: string } +>isText : Symbol(isText, Decl(discriminatedUnionDestructuringWithDefaults.ts, 14, 5)) +>children : Symbol(children, Decl(discriminatedUnionDestructuringWithDefaults.ts, 14, 19)) + + | { isText: false; children: number } +>isText : Symbol(isText, Decl(discriminatedUnionDestructuringWithDefaults.ts, 15, 5)) +>children : Symbol(children, Decl(discriminatedUnionDestructuringWithDefaults.ts, 15, 20)) + +) { + if (isText === true) { +>isText : Symbol(isText, Decl(discriminatedUnionDestructuringWithDefaults.ts, 13, 18)) + + let data: string = children; +>data : Symbol(data, Decl(discriminatedUnionDestructuringWithDefaults.ts, 18, 11)) +>children : Symbol(children, Decl(discriminatedUnionDestructuringWithDefaults.ts, 13, 26)) + + } else if (isText === false) { +>isText : Symbol(isText, Decl(discriminatedUnionDestructuringWithDefaults.ts, 13, 18)) + + let data: number = children; +>data : Symbol(data, Decl(discriminatedUnionDestructuringWithDefaults.ts, 20, 11)) +>children : Symbol(children, Decl(discriminatedUnionDestructuringWithDefaults.ts, 13, 26)) + } +} + +// Case 3: whzx5byb's soundness check — default foreign to one arm must still error +function soundnessCheck({ isText = false, children = true }: // expect error on `children = true` +>soundnessCheck : Symbol(soundnessCheck, Decl(discriminatedUnionDestructuringWithDefaults.ts, 22, 1)) +>isText : Symbol(isText, Decl(discriminatedUnionDestructuringWithDefaults.ts, 25, 25)) +>children : Symbol(children, Decl(discriminatedUnionDestructuringWithDefaults.ts, 25, 41)) + + { isText: true; children?: string } +>isText : Symbol(isText, Decl(discriminatedUnionDestructuringWithDefaults.ts, 26, 5)) +>children : Symbol(children, Decl(discriminatedUnionDestructuringWithDefaults.ts, 26, 19)) + + | { isText: false; children?: number } +>isText : Symbol(isText, Decl(discriminatedUnionDestructuringWithDefaults.ts, 27, 5)) +>children : Symbol(children, Decl(discriminatedUnionDestructuringWithDefaults.ts, 27, 20)) + +) { + if (isText === true) { +>isText : Symbol(isText, Decl(discriminatedUnionDestructuringWithDefaults.ts, 25, 25)) + + let data: string = children; +>data : Symbol(data, Decl(discriminatedUnionDestructuringWithDefaults.ts, 30, 11)) +>children : Symbol(children, Decl(discriminatedUnionDestructuringWithDefaults.ts, 25, 41)) + } +} + diff --git a/tests/baselines/reference/discriminatedUnionDestructuringWithDefaults.types b/tests/baselines/reference/discriminatedUnionDestructuringWithDefaults.types new file mode 100644 index 0000000000000..b4a7705350a45 --- /dev/null +++ b/tests/baselines/reference/discriminatedUnionDestructuringWithDefaults.types @@ -0,0 +1,165 @@ +//// [tests/cases/compiler/discriminatedUnionDestructuringWithDefaults.ts] //// + +=== discriminatedUnionDestructuringWithDefaults.ts === +// Case 1: the issue #50139 repro — must compile clean after the fix +function repro({ isText = false, children = 0 }: +>repro : ({ isText, children }: { isText: true; children?: string; } | { isText: false; children?: number; }) => void +> : ^ ^^ ^^^^^^^^^ +>isText : boolean +> : ^^^^^^^ +>false : false +> : ^^^^^ +>children : string | number +> : ^^^^^^^^^^^^^^^ +>0 : 0 +> : ^ + + { isText: true; children?: string } +>isText : true +> : ^^^^ +>true : true +> : ^^^^ +>children : string | undefined +> : ^^^^^^^^^^^^^^^^^^ + + | { isText: false; children?: number } +>isText : false +> : ^^^^^ +>false : false +> : ^^^^^ +>children : number | undefined +> : ^^^^^^^^^^^^^^^^^^ + +) { + if (isText === true) { +>isText === true : boolean +> : ^^^^^^^ +>isText : boolean +> : ^^^^^^^ +>true : true +> : ^^^^ + + let data: string = children; +>data : string +> : ^^^^^^ +>children : string +> : ^^^^^^ + + } else if (isText === false) { +>isText === false : boolean +> : ^^^^^^^ +>isText : false +> : ^^^^^ +>false : false +> : ^^^^^ + + let data: number = children; +>data : number +> : ^^^^^^ +>children : number +> : ^^^^^^ + } +} + +// Case 2: control — no defaults, already worked, must not regress +function control({ isText, children }: +>control : ({ isText, children }: { isText: true; children: string; } | { isText: false; children: number; }) => void +> : ^ ^^ ^^^^^^^^^ +>isText : boolean +> : ^^^^^^^ +>children : string | number +> : ^^^^^^^^^^^^^^^ + + { isText: true; children: string } +>isText : true +> : ^^^^ +>true : true +> : ^^^^ +>children : string +> : ^^^^^^ + + | { isText: false; children: number } +>isText : false +> : ^^^^^ +>false : false +> : ^^^^^ +>children : number +> : ^^^^^^ + +) { + if (isText === true) { +>isText === true : boolean +> : ^^^^^^^ +>isText : boolean +> : ^^^^^^^ +>true : true +> : ^^^^ + + let data: string = children; +>data : string +> : ^^^^^^ +>children : string +> : ^^^^^^ + + } else if (isText === false) { +>isText === false : boolean +> : ^^^^^^^ +>isText : false +> : ^^^^^ +>false : false +> : ^^^^^ + + let data: number = children; +>data : number +> : ^^^^^^ +>children : number +> : ^^^^^^ + } +} + +// Case 3: whzx5byb's soundness check — default foreign to one arm must still error +function soundnessCheck({ isText = false, children = true }: // expect error on `children = true` +>soundnessCheck : ({ isText, children }: { isText: true; children?: string; } | { isText: false; children?: number; }) => void +> : ^ ^^ ^^^^^^^^^ +>isText : boolean +> : ^^^^^^^ +>false : false +> : ^^^^^ +>children : string | number +> : ^^^^^^^^^^^^^^^ +>true : true +> : ^^^^ + + { isText: true; children?: string } +>isText : true +> : ^^^^ +>true : true +> : ^^^^ +>children : string | undefined +> : ^^^^^^^^^^^^^^^^^^ + + | { isText: false; children?: number } +>isText : false +> : ^^^^^ +>false : false +> : ^^^^^ +>children : number | undefined +> : ^^^^^^^^^^^^^^^^^^ + +) { + if (isText === true) { +>isText === true : boolean +> : ^^^^^^^ +>isText : boolean +> : ^^^^^^^ +>true : true +> : ^^^^ + + let data: string = children; +>data : string +> : ^^^^^^ +>children : string +> : ^^^^^^ + } +} + diff --git a/tests/cases/compiler/discriminatedUnionDestructuringWithDefaults.ts b/tests/cases/compiler/discriminatedUnionDestructuringWithDefaults.ts new file mode 100644 index 0000000000000..8862b23e47beb --- /dev/null +++ b/tests/cases/compiler/discriminatedUnionDestructuringWithDefaults.ts @@ -0,0 +1,35 @@ +// @strict: true + +// Case 1: the issue #50139 repro — must compile clean after the fix +function repro({ isText = false, children = 0 }: + { isText: true; children?: string } + | { isText: false; children?: number } +) { + if (isText === true) { + let data: string = children; + } else if (isText === false) { + let data: number = children; + } +} + +// Case 2: control — no defaults, already worked, must not regress +function control({ isText, children }: + { isText: true; children: string } + | { isText: false; children: number } +) { + if (isText === true) { + let data: string = children; + } else if (isText === false) { + let data: number = children; + } +} + +// Case 3: whzx5byb's soundness check — default foreign to one arm must still error +function soundnessCheck({ isText = false, children = true }: // expect error on `children = true` + { isText: true; children?: string } + | { isText: false; children?: number } +) { + if (isText === true) { + let data: string = children; + } +}