diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 2f7332c181e02..a91da4c8bbf73 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -20366,7 +20366,7 @@ namespace ts { } if (isUnitType(valueType)) { const regularType = getRegularTypeOfLiteralType(valueType); - return filterType(type, t => getRegularTypeOfLiteralType(t) !== regularType); + return filterType(type, t => isUnitType(t) ? !areTypesComparable(t, valueType) : getRegularTypeOfLiteralType(t) !== regularType); } return type; } diff --git a/tests/baselines/reference/discriminantsAndPrimitives.js b/tests/baselines/reference/discriminantsAndPrimitives.js index d72ac0ff9f974..dd8e07b495542 100644 --- a/tests/baselines/reference/discriminantsAndPrimitives.js +++ b/tests/baselines/reference/discriminantsAndPrimitives.js @@ -45,7 +45,40 @@ function f4(x: Foo | Bar | string | number | null) { x.name; } } -} +} + +// Repro from #31319 + +const enum EnumTypeNode { + Pattern = "Pattern", + Disjunction = "Disjunction", +} + +type NodeA = Disjunction | Pattern; + +interface NodeBase { + type: NodeA["type"] +} + +interface Disjunction extends NodeBase { + type: EnumTypeNode.Disjunction + alternatives: string[] +} + +interface Pattern extends NodeBase { + type: EnumTypeNode.Pattern + elements: string[] +} + +let n!: NodeA + +if (n.type === "Disjunction") { + n.alternatives.slice() +} +else { + n.elements.slice() // n should be narrowed to Pattern +} + //// [discriminantsAndPrimitives.js] // Repro from #10257 plus other tests @@ -81,3 +114,10 @@ function f4(x) { } } } +var n; +if (n.type === "Disjunction") { + n.alternatives.slice(); +} +else { + n.elements.slice(); // n should be narrowed to Pattern +} diff --git a/tests/baselines/reference/discriminantsAndPrimitives.symbols b/tests/baselines/reference/discriminantsAndPrimitives.symbols index 818fbc663fff9..b0f302fbd087f 100644 --- a/tests/baselines/reference/discriminantsAndPrimitives.symbols +++ b/tests/baselines/reference/discriminantsAndPrimitives.symbols @@ -114,3 +114,80 @@ function f4(x: Foo | Bar | string | number | null) { } } } + +// Repro from #31319 + +const enum EnumTypeNode { +>EnumTypeNode : Symbol(EnumTypeNode, Decl(discriminantsAndPrimitives.ts, 46, 1)) + + Pattern = "Pattern", +>Pattern : Symbol(EnumTypeNode.Pattern, Decl(discriminantsAndPrimitives.ts, 50, 25)) + + Disjunction = "Disjunction", +>Disjunction : Symbol(EnumTypeNode.Disjunction, Decl(discriminantsAndPrimitives.ts, 51, 24)) +} + +type NodeA = Disjunction | Pattern; +>NodeA : Symbol(NodeA, Decl(discriminantsAndPrimitives.ts, 53, 1)) +>Disjunction : Symbol(Disjunction, Decl(discriminantsAndPrimitives.ts, 59, 1)) +>Pattern : Symbol(Pattern, Decl(discriminantsAndPrimitives.ts, 64, 1)) + +interface NodeBase { +>NodeBase : Symbol(NodeBase, Decl(discriminantsAndPrimitives.ts, 55, 35)) + + type: NodeA["type"] +>type : Symbol(NodeBase.type, Decl(discriminantsAndPrimitives.ts, 57, 20)) +>NodeA : Symbol(NodeA, Decl(discriminantsAndPrimitives.ts, 53, 1)) +} + +interface Disjunction extends NodeBase { +>Disjunction : Symbol(Disjunction, Decl(discriminantsAndPrimitives.ts, 59, 1)) +>NodeBase : Symbol(NodeBase, Decl(discriminantsAndPrimitives.ts, 55, 35)) + + type: EnumTypeNode.Disjunction +>type : Symbol(Disjunction.type, Decl(discriminantsAndPrimitives.ts, 61, 40)) +>EnumTypeNode : Symbol(EnumTypeNode, Decl(discriminantsAndPrimitives.ts, 46, 1)) +>Disjunction : Symbol(EnumTypeNode.Disjunction, Decl(discriminantsAndPrimitives.ts, 51, 24)) + + alternatives: string[] +>alternatives : Symbol(Disjunction.alternatives, Decl(discriminantsAndPrimitives.ts, 62, 34)) +} + +interface Pattern extends NodeBase { +>Pattern : Symbol(Pattern, Decl(discriminantsAndPrimitives.ts, 64, 1)) +>NodeBase : Symbol(NodeBase, Decl(discriminantsAndPrimitives.ts, 55, 35)) + + type: EnumTypeNode.Pattern +>type : Symbol(Pattern.type, Decl(discriminantsAndPrimitives.ts, 66, 36)) +>EnumTypeNode : Symbol(EnumTypeNode, Decl(discriminantsAndPrimitives.ts, 46, 1)) +>Pattern : Symbol(EnumTypeNode.Pattern, Decl(discriminantsAndPrimitives.ts, 50, 25)) + + elements: string[] +>elements : Symbol(Pattern.elements, Decl(discriminantsAndPrimitives.ts, 67, 30)) +} + +let n!: NodeA +>n : Symbol(n, Decl(discriminantsAndPrimitives.ts, 71, 3)) +>NodeA : Symbol(NodeA, Decl(discriminantsAndPrimitives.ts, 53, 1)) + +if (n.type === "Disjunction") { +>n.type : Symbol(type, Decl(discriminantsAndPrimitives.ts, 61, 40), Decl(discriminantsAndPrimitives.ts, 66, 36)) +>n : Symbol(n, Decl(discriminantsAndPrimitives.ts, 71, 3)) +>type : Symbol(type, Decl(discriminantsAndPrimitives.ts, 61, 40), Decl(discriminantsAndPrimitives.ts, 66, 36)) + + n.alternatives.slice() +>n.alternatives.slice : Symbol(Array.slice, Decl(lib.es5.d.ts, --, --)) +>n.alternatives : Symbol(Disjunction.alternatives, Decl(discriminantsAndPrimitives.ts, 62, 34)) +>n : Symbol(n, Decl(discriminantsAndPrimitives.ts, 71, 3)) +>alternatives : Symbol(Disjunction.alternatives, Decl(discriminantsAndPrimitives.ts, 62, 34)) +>slice : Symbol(Array.slice, Decl(lib.es5.d.ts, --, --)) +} +else { + n.elements.slice() // n should be narrowed to Pattern +>n.elements.slice : Symbol(Array.slice, Decl(lib.es5.d.ts, --, --)) +>n.elements : Symbol(Pattern.elements, Decl(discriminantsAndPrimitives.ts, 67, 30)) +>n : Symbol(n, Decl(discriminantsAndPrimitives.ts, 71, 3)) +>elements : Symbol(Pattern.elements, Decl(discriminantsAndPrimitives.ts, 67, 30)) +>slice : Symbol(Array.slice, Decl(lib.es5.d.ts, --, --)) +} + diff --git a/tests/baselines/reference/discriminantsAndPrimitives.types b/tests/baselines/reference/discriminantsAndPrimitives.types index 6498df3926e3e..d738f0f401327 100644 --- a/tests/baselines/reference/discriminantsAndPrimitives.types +++ b/tests/baselines/reference/discriminantsAndPrimitives.types @@ -126,3 +126,72 @@ function f4(x: Foo | Bar | string | number | null) { } } } + +// Repro from #31319 + +const enum EnumTypeNode { +>EnumTypeNode : EnumTypeNode + + Pattern = "Pattern", +>Pattern : EnumTypeNode.Pattern +>"Pattern" : "Pattern" + + Disjunction = "Disjunction", +>Disjunction : EnumTypeNode.Disjunction +>"Disjunction" : "Disjunction" +} + +type NodeA = Disjunction | Pattern; +>NodeA : NodeA + +interface NodeBase { + type: NodeA["type"] +>type : EnumTypeNode +} + +interface Disjunction extends NodeBase { + type: EnumTypeNode.Disjunction +>type : EnumTypeNode.Disjunction +>EnumTypeNode : any + + alternatives: string[] +>alternatives : string[] +} + +interface Pattern extends NodeBase { + type: EnumTypeNode.Pattern +>type : EnumTypeNode.Pattern +>EnumTypeNode : any + + elements: string[] +>elements : string[] +} + +let n!: NodeA +>n : NodeA + +if (n.type === "Disjunction") { +>n.type === "Disjunction" : boolean +>n.type : EnumTypeNode +>n : NodeA +>type : EnumTypeNode +>"Disjunction" : "Disjunction" + + n.alternatives.slice() +>n.alternatives.slice() : string[] +>n.alternatives.slice : (start?: number | undefined, end?: number | undefined) => string[] +>n.alternatives : string[] +>n : Disjunction +>alternatives : string[] +>slice : (start?: number | undefined, end?: number | undefined) => string[] +} +else { + n.elements.slice() // n should be narrowed to Pattern +>n.elements.slice() : string[] +>n.elements.slice : (start?: number | undefined, end?: number | undefined) => string[] +>n.elements : string[] +>n : Pattern +>elements : string[] +>slice : (start?: number | undefined, end?: number | undefined) => string[] +} + diff --git a/tests/baselines/reference/unusedVariablesWithUnderscoreInForOfLoop.types b/tests/baselines/reference/unusedVariablesWithUnderscoreInForOfLoop.types index 3ba565b79145d..eb1ce0b0bbeae 100644 --- a/tests/baselines/reference/unusedVariablesWithUnderscoreInForOfLoop.types +++ b/tests/baselines/reference/unusedVariablesWithUnderscoreInForOfLoop.types @@ -12,9 +12,9 @@ function f() { console.log(b); >console.log(b) : void ->console.log : (message?: any, ...optionalParams: any[]) => void +>console.log : (...data: any[]) => void >console : Console ->log : (message?: any, ...optionalParams: any[]) => void +>log : (...data: any[]) => void >b : string | number } @@ -28,9 +28,9 @@ function f() { console.log(a); >console.log(a) : void ->console.log : (message?: any, ...optionalParams: any[]) => void +>console.log : (...data: any[]) => void >console : Console ->log : (message?: any, ...optionalParams: any[]) => void +>log : (...data: any[]) => void >a : string | number } diff --git a/tests/cases/compiler/discriminantsAndPrimitives.ts b/tests/cases/compiler/discriminantsAndPrimitives.ts index 6352d741808ab..b147b47646402 100644 --- a/tests/cases/compiler/discriminantsAndPrimitives.ts +++ b/tests/cases/compiler/discriminantsAndPrimitives.ts @@ -46,4 +46,36 @@ function f4(x: Foo | Bar | string | number | null) { x.name; } } -} \ No newline at end of file +} + +// Repro from #31319 + +const enum EnumTypeNode { + Pattern = "Pattern", + Disjunction = "Disjunction", +} + +type NodeA = Disjunction | Pattern; + +interface NodeBase { + type: NodeA["type"] +} + +interface Disjunction extends NodeBase { + type: EnumTypeNode.Disjunction + alternatives: string[] +} + +interface Pattern extends NodeBase { + type: EnumTypeNode.Pattern + elements: string[] +} + +let n!: NodeA + +if (n.type === "Disjunction") { + n.alternatives.slice() +} +else { + n.elements.slice() // n should be narrowed to Pattern +}