diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index cb52a13176aca..7079ecf8a48f7 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -25157,6 +25157,9 @@ namespace ts { if (parentType && parentType.flags & TypeFlags.Union && !(parent.kind === SyntaxKind.Parameter && isSymbolAssigned(symbol))) { const pattern = declaration.parent; const narrowedType = getFlowTypeOfReference(pattern, parentType, parentType, /*flowContainer*/ undefined, location.flowNode); + if (narrowedType.flags & TypeFlags.Never) { + return neverType; + } return getBindingElementTypeFromParentType(declaration, narrowedType); } } diff --git a/tests/baselines/reference/arrayDestructuringInSwitch1.js b/tests/baselines/reference/arrayDestructuringInSwitch1.js new file mode 100644 index 0000000000000..1947d7583e346 --- /dev/null +++ b/tests/baselines/reference/arrayDestructuringInSwitch1.js @@ -0,0 +1,47 @@ +//// [arrayDestructuringInSwitch1.ts] +export type Expression = BooleanLogicExpression | 'true' | 'false'; +export type BooleanLogicExpression = ['and', ...Expression[]] | ['not', Expression]; + +export function evaluate(expression: Expression): boolean { + if (Array.isArray(expression)) { + const [operator, ...operands] = expression; + switch (operator) { + case 'and': { + return operands.every((child) => evaluate(child)); + } + case 'not': { + return !evaluate(operands[0]); + } + default: { + throw new Error(`${operator} is not a supported operator`); + } + } + } else { + return expression === 'true'; + } +} + +//// [arrayDestructuringInSwitch1.js] +"use strict"; +exports.__esModule = true; +exports.evaluate = void 0; +function evaluate(expression) { + if (Array.isArray(expression)) { + var operator = expression[0], operands = expression.slice(1); + switch (operator) { + case 'and': { + return operands.every(function (child) { return evaluate(child); }); + } + case 'not': { + return !evaluate(operands[0]); + } + default: { + throw new Error("".concat(operator, " is not a supported operator")); + } + } + } + else { + return expression === 'true'; + } +} +exports.evaluate = evaluate; diff --git a/tests/baselines/reference/arrayDestructuringInSwitch1.symbols b/tests/baselines/reference/arrayDestructuringInSwitch1.symbols new file mode 100644 index 0000000000000..f1a9bea3fa35f --- /dev/null +++ b/tests/baselines/reference/arrayDestructuringInSwitch1.symbols @@ -0,0 +1,55 @@ +=== tests/cases/compiler/arrayDestructuringInSwitch1.ts === +export type Expression = BooleanLogicExpression | 'true' | 'false'; +>Expression : Symbol(Expression, Decl(arrayDestructuringInSwitch1.ts, 0, 0)) +>BooleanLogicExpression : Symbol(BooleanLogicExpression, Decl(arrayDestructuringInSwitch1.ts, 0, 67)) + +export type BooleanLogicExpression = ['and', ...Expression[]] | ['not', Expression]; +>BooleanLogicExpression : Symbol(BooleanLogicExpression, Decl(arrayDestructuringInSwitch1.ts, 0, 67)) +>Expression : Symbol(Expression, Decl(arrayDestructuringInSwitch1.ts, 0, 0)) +>Expression : Symbol(Expression, Decl(arrayDestructuringInSwitch1.ts, 0, 0)) + +export function evaluate(expression: Expression): boolean { +>evaluate : Symbol(evaluate, Decl(arrayDestructuringInSwitch1.ts, 1, 84)) +>expression : Symbol(expression, Decl(arrayDestructuringInSwitch1.ts, 3, 25)) +>Expression : Symbol(Expression, Decl(arrayDestructuringInSwitch1.ts, 0, 0)) + + if (Array.isArray(expression)) { +>Array.isArray : Symbol(ArrayConstructor.isArray, Decl(lib.es5.d.ts, --, --)) +>Array : Symbol(Array, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>isArray : Symbol(ArrayConstructor.isArray, Decl(lib.es5.d.ts, --, --)) +>expression : Symbol(expression, Decl(arrayDestructuringInSwitch1.ts, 3, 25)) + + const [operator, ...operands] = expression; +>operator : Symbol(operator, Decl(arrayDestructuringInSwitch1.ts, 5, 11)) +>operands : Symbol(operands, Decl(arrayDestructuringInSwitch1.ts, 5, 20)) +>expression : Symbol(expression, Decl(arrayDestructuringInSwitch1.ts, 3, 25)) + + switch (operator) { +>operator : Symbol(operator, Decl(arrayDestructuringInSwitch1.ts, 5, 11)) + + case 'and': { + return operands.every((child) => evaluate(child)); +>operands.every : Symbol(Array.every, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>operands : Symbol(operands, Decl(arrayDestructuringInSwitch1.ts, 5, 20)) +>every : Symbol(Array.every, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>child : Symbol(child, Decl(arrayDestructuringInSwitch1.ts, 8, 31)) +>evaluate : Symbol(evaluate, Decl(arrayDestructuringInSwitch1.ts, 1, 84)) +>child : Symbol(child, Decl(arrayDestructuringInSwitch1.ts, 8, 31)) + } + case 'not': { + return !evaluate(operands[0]); +>evaluate : Symbol(evaluate, Decl(arrayDestructuringInSwitch1.ts, 1, 84)) +>operands : Symbol(operands, Decl(arrayDestructuringInSwitch1.ts, 5, 20)) +>0 : Symbol(0) + } + default: { + throw new Error(`${operator} is not a supported operator`); +>Error : Symbol(Error, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>operator : Symbol(operator, Decl(arrayDestructuringInSwitch1.ts, 5, 11)) + } + } + } else { + return expression === 'true'; +>expression : Symbol(expression, Decl(arrayDestructuringInSwitch1.ts, 3, 25)) + } +} diff --git a/tests/baselines/reference/arrayDestructuringInSwitch1.types b/tests/baselines/reference/arrayDestructuringInSwitch1.types new file mode 100644 index 0000000000000..dfb06774cec16 --- /dev/null +++ b/tests/baselines/reference/arrayDestructuringInSwitch1.types @@ -0,0 +1,66 @@ +=== tests/cases/compiler/arrayDestructuringInSwitch1.ts === +export type Expression = BooleanLogicExpression | 'true' | 'false'; +>Expression : Expression + +export type BooleanLogicExpression = ['and', ...Expression[]] | ['not', Expression]; +>BooleanLogicExpression : BooleanLogicExpression + +export function evaluate(expression: Expression): boolean { +>evaluate : (expression: Expression) => boolean +>expression : Expression + + if (Array.isArray(expression)) { +>Array.isArray(expression) : boolean +>Array.isArray : (arg: any) => arg is any[] +>Array : ArrayConstructor +>isArray : (arg: any) => arg is any[] +>expression : Expression + + const [operator, ...operands] = expression; +>operator : "and" | "not" +>operands : Expression[] | [Expression] +>expression : BooleanLogicExpression + + switch (operator) { +>operator : "and" | "not" + + case 'and': { +>'and' : "and" + + return operands.every((child) => evaluate(child)); +>operands.every((child) => evaluate(child)) : boolean +>operands.every : { (predicate: (value: Expression, index: number, array: Expression[]) => value is S, thisArg?: any): this is S[]; (predicate: (value: Expression, index: number, array: Expression[]) => unknown, thisArg?: any): boolean; } | { (predicate: (value: Expression, index: number, array: Expression[]) => value is S, thisArg?: any): this is S[]; (predicate: (value: Expression, index: number, array: Expression[]) => unknown, thisArg?: any): boolean; } +>operands : Expression[] | [Expression] +>every : { (predicate: (value: Expression, index: number, array: Expression[]) => value is S, thisArg?: any): this is S[]; (predicate: (value: Expression, index: number, array: Expression[]) => unknown, thisArg?: any): boolean; } | { (predicate: (value: Expression, index: number, array: Expression[]) => value is S, thisArg?: any): this is S[]; (predicate: (value: Expression, index: number, array: Expression[]) => unknown, thisArg?: any): boolean; } +>(child) => evaluate(child) : (child: Expression) => boolean +>child : Expression +>evaluate(child) : boolean +>evaluate : (expression: Expression) => boolean +>child : Expression + } + case 'not': { +>'not' : "not" + + return !evaluate(operands[0]); +>!evaluate(operands[0]) : boolean +>evaluate(operands[0]) : boolean +>evaluate : (expression: Expression) => boolean +>operands[0] : Expression +>operands : Expression[] | [Expression] +>0 : 0 + } + default: { + throw new Error(`${operator} is not a supported operator`); +>new Error(`${operator} is not a supported operator`) : Error +>Error : ErrorConstructor +>`${operator} is not a supported operator` : string +>operator : never + } + } + } else { + return expression === 'true'; +>expression === 'true' : boolean +>expression : "true" | "false" +>'true' : "true" + } +} diff --git a/tests/baselines/reference/arrayDestructuringInSwitch2.errors.txt b/tests/baselines/reference/arrayDestructuringInSwitch2.errors.txt new file mode 100644 index 0000000000000..48fb17f0c6edc --- /dev/null +++ b/tests/baselines/reference/arrayDestructuringInSwitch2.errors.txt @@ -0,0 +1,20 @@ +tests/cases/compiler/arrayDestructuringInSwitch2.ts(11,13): error TS2488: Type 'never' must have a '[Symbol.iterator]()' method that returns an iterator. + + +==== tests/cases/compiler/arrayDestructuringInSwitch2.ts (1 errors) ==== + type X = { kind: "a", a: [1] } | { kind: "b", a: [] } + + function foo(x: X): 1 { + const { kind, a } = x; + switch (kind) { + case "a": + return a[0]; + case "b": + return 1; + default: + const [n] = a; + ~~~ +!!! error TS2488: Type 'never' must have a '[Symbol.iterator]()' method that returns an iterator. + return a; + } + } \ No newline at end of file diff --git a/tests/baselines/reference/arrayDestructuringInSwitch2.js b/tests/baselines/reference/arrayDestructuringInSwitch2.js new file mode 100644 index 0000000000000..be6f00c08416a --- /dev/null +++ b/tests/baselines/reference/arrayDestructuringInSwitch2.js @@ -0,0 +1,29 @@ +//// [arrayDestructuringInSwitch2.ts] +type X = { kind: "a", a: [1] } | { kind: "b", a: [] } + +function foo(x: X): 1 { + const { kind, a } = x; + switch (kind) { + case "a": + return a[0]; + case "b": + return 1; + default: + const [n] = a; + return a; + } +} + +//// [arrayDestructuringInSwitch2.js] +function foo(x) { + var kind = x.kind, a = x.a; + switch (kind) { + case "a": + return a[0]; + case "b": + return 1; + default: + var n = a[0]; + return a; + } +} diff --git a/tests/baselines/reference/arrayDestructuringInSwitch2.symbols b/tests/baselines/reference/arrayDestructuringInSwitch2.symbols new file mode 100644 index 0000000000000..7d64ebdb8a432 --- /dev/null +++ b/tests/baselines/reference/arrayDestructuringInSwitch2.symbols @@ -0,0 +1,37 @@ +=== tests/cases/compiler/arrayDestructuringInSwitch2.ts === +type X = { kind: "a", a: [1] } | { kind: "b", a: [] } +>X : Symbol(X, Decl(arrayDestructuringInSwitch2.ts, 0, 0)) +>kind : Symbol(kind, Decl(arrayDestructuringInSwitch2.ts, 0, 10)) +>a : Symbol(a, Decl(arrayDestructuringInSwitch2.ts, 0, 21)) +>kind : Symbol(kind, Decl(arrayDestructuringInSwitch2.ts, 0, 34)) +>a : Symbol(a, Decl(arrayDestructuringInSwitch2.ts, 0, 45)) + +function foo(x: X): 1 { +>foo : Symbol(foo, Decl(arrayDestructuringInSwitch2.ts, 0, 53)) +>x : Symbol(x, Decl(arrayDestructuringInSwitch2.ts, 2, 13)) +>X : Symbol(X, Decl(arrayDestructuringInSwitch2.ts, 0, 0)) + + const { kind, a } = x; +>kind : Symbol(kind, Decl(arrayDestructuringInSwitch2.ts, 3, 9)) +>a : Symbol(a, Decl(arrayDestructuringInSwitch2.ts, 3, 15)) +>x : Symbol(x, Decl(arrayDestructuringInSwitch2.ts, 2, 13)) + + switch (kind) { +>kind : Symbol(kind, Decl(arrayDestructuringInSwitch2.ts, 3, 9)) + + case "a": + return a[0]; +>a : Symbol(a, Decl(arrayDestructuringInSwitch2.ts, 3, 15)) +>0 : Symbol(0) + + case "b": + return 1; + default: + const [n] = a; +>n : Symbol(n, Decl(arrayDestructuringInSwitch2.ts, 10, 13)) +>a : Symbol(a, Decl(arrayDestructuringInSwitch2.ts, 3, 15)) + + return a; +>a : Symbol(a, Decl(arrayDestructuringInSwitch2.ts, 3, 15)) + } +} diff --git a/tests/baselines/reference/arrayDestructuringInSwitch2.types b/tests/baselines/reference/arrayDestructuringInSwitch2.types new file mode 100644 index 0000000000000..727a2fc68ebc3 --- /dev/null +++ b/tests/baselines/reference/arrayDestructuringInSwitch2.types @@ -0,0 +1,43 @@ +=== tests/cases/compiler/arrayDestructuringInSwitch2.ts === +type X = { kind: "a", a: [1] } | { kind: "b", a: [] } +>X : X +>kind : "a" +>a : [1] +>kind : "b" +>a : [] + +function foo(x: X): 1 { +>foo : (x: X) => 1 +>x : X + + const { kind, a } = x; +>kind : "a" | "b" +>a : [1] | [] +>x : X + + switch (kind) { +>kind : "a" | "b" + + case "a": +>"a" : "a" + + return a[0]; +>a[0] : 1 +>a : [1] +>0 : 0 + + case "b": +>"b" : "b" + + return 1; +>1 : 1 + + default: + const [n] = a; +>n : never +>a : never + + return a; +>a : never + } +} diff --git a/tests/cases/compiler/arrayDestructuringInSwitch1.ts b/tests/cases/compiler/arrayDestructuringInSwitch1.ts new file mode 100644 index 0000000000000..47d77f97d513e --- /dev/null +++ b/tests/cases/compiler/arrayDestructuringInSwitch1.ts @@ -0,0 +1,21 @@ +export type Expression = BooleanLogicExpression | 'true' | 'false'; +export type BooleanLogicExpression = ['and', ...Expression[]] | ['not', Expression]; + +export function evaluate(expression: Expression): boolean { + if (Array.isArray(expression)) { + const [operator, ...operands] = expression; + switch (operator) { + case 'and': { + return operands.every((child) => evaluate(child)); + } + case 'not': { + return !evaluate(operands[0]); + } + default: { + throw new Error(`${operator} is not a supported operator`); + } + } + } else { + return expression === 'true'; + } +} \ No newline at end of file diff --git a/tests/cases/compiler/arrayDestructuringInSwitch2.ts b/tests/cases/compiler/arrayDestructuringInSwitch2.ts new file mode 100644 index 0000000000000..ec084295d0907 --- /dev/null +++ b/tests/cases/compiler/arrayDestructuringInSwitch2.ts @@ -0,0 +1,14 @@ +type X = { kind: "a", a: [1] } | { kind: "b", a: [] } + +function foo(x: X): 1 { + const { kind, a } = x; + switch (kind) { + case "a": + return a[0]; + case "b": + return 1; + default: + const [n] = a; + return a; + } +} \ No newline at end of file