From 2c0c128e981137aeaadfb37cf7c71bd62889a555 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Tue, 21 Nov 2023 21:24:02 +0100 Subject: [PATCH] Avoid incorrect narrowings using const variables from binding elemens with literal initializers (#56347) --- src/compiler/checker.ts | 3 +- ...ndingElementWithLiteralInitializer.symbols | 24 ++++++++++++++++ ...BindingElementWithLiteralInitializer.types | 28 +++++++++++++++++++ ...romBindingElementWithLiteralInitializer.ts | 12 ++++++++ 4 files changed, 66 insertions(+), 1 deletion(-) create mode 100644 tests/baselines/reference/avoidNarrowingUsingConstVariableFromBindingElementWithLiteralInitializer.symbols create mode 100644 tests/baselines/reference/avoidNarrowingUsingConstVariableFromBindingElementWithLiteralInitializer.types create mode 100644 tests/cases/compiler/avoidNarrowingUsingConstVariableFromBindingElementWithLiteralInitializer.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 9831899a1b2..8ae0ea8980e 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -26214,7 +26214,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { if (hasOnlyExpressionInitializer(declaration) && isBlockScopedNameDeclaredBeforeUse(declaration, node)) { const initializer = getEffectiveInitializer(declaration); if (initializer) { - return tryGetNameFromType(getTypeOfExpression(initializer)); + const initializerType = isBindingPattern(declaration.parent) ? getTypeForBindingElement(declaration as BindingElement) : getTypeOfExpression(initializer); + return initializerType && tryGetNameFromType(initializerType); } if (isEnumMember(declaration)) { return getTextOfPropertyName(declaration.name); diff --git a/tests/baselines/reference/avoidNarrowingUsingConstVariableFromBindingElementWithLiteralInitializer.symbols b/tests/baselines/reference/avoidNarrowingUsingConstVariableFromBindingElementWithLiteralInitializer.symbols new file mode 100644 index 00000000000..a273d75e485 --- /dev/null +++ b/tests/baselines/reference/avoidNarrowingUsingConstVariableFromBindingElementWithLiteralInitializer.symbols @@ -0,0 +1,24 @@ +//// [tests/cases/compiler/avoidNarrowingUsingConstVariableFromBindingElementWithLiteralInitializer.ts] //// + +=== avoidNarrowingUsingConstVariableFromBindingElementWithLiteralInitializer.ts === +declare const foo: ["a", string, number] | ["b", string, boolean]; +>foo : Symbol(foo, Decl(avoidNarrowingUsingConstVariableFromBindingElementWithLiteralInitializer.ts, 0, 13)) + +export function test(arg: { index?: number }) { +>test : Symbol(test, Decl(avoidNarrowingUsingConstVariableFromBindingElementWithLiteralInitializer.ts, 0, 66)) +>arg : Symbol(arg, Decl(avoidNarrowingUsingConstVariableFromBindingElementWithLiteralInitializer.ts, 2, 21)) +>index : Symbol(index, Decl(avoidNarrowingUsingConstVariableFromBindingElementWithLiteralInitializer.ts, 2, 27)) + + const { index = 0 } = arg; +>index : Symbol(index, Decl(avoidNarrowingUsingConstVariableFromBindingElementWithLiteralInitializer.ts, 3, 9)) +>arg : Symbol(arg, Decl(avoidNarrowingUsingConstVariableFromBindingElementWithLiteralInitializer.ts, 2, 21)) + + if (foo[index] === "a") { +>foo : Symbol(foo, Decl(avoidNarrowingUsingConstVariableFromBindingElementWithLiteralInitializer.ts, 0, 13)) +>index : Symbol(index, Decl(avoidNarrowingUsingConstVariableFromBindingElementWithLiteralInitializer.ts, 3, 9)) + + foo; +>foo : Symbol(foo, Decl(avoidNarrowingUsingConstVariableFromBindingElementWithLiteralInitializer.ts, 0, 13)) + } +} + diff --git a/tests/baselines/reference/avoidNarrowingUsingConstVariableFromBindingElementWithLiteralInitializer.types b/tests/baselines/reference/avoidNarrowingUsingConstVariableFromBindingElementWithLiteralInitializer.types new file mode 100644 index 00000000000..24155c1a020 --- /dev/null +++ b/tests/baselines/reference/avoidNarrowingUsingConstVariableFromBindingElementWithLiteralInitializer.types @@ -0,0 +1,28 @@ +//// [tests/cases/compiler/avoidNarrowingUsingConstVariableFromBindingElementWithLiteralInitializer.ts] //// + +=== avoidNarrowingUsingConstVariableFromBindingElementWithLiteralInitializer.ts === +declare const foo: ["a", string, number] | ["b", string, boolean]; +>foo : ["a", string, number] | ["b", string, boolean] + +export function test(arg: { index?: number }) { +>test : (arg: { index?: number | undefined; }) => void +>arg : { index?: number | undefined; } +>index : number | undefined + + const { index = 0 } = arg; +>index : number +>0 : 0 +>arg : { index?: number | undefined; } + + if (foo[index] === "a") { +>foo[index] === "a" : boolean +>foo[index] : string | number | boolean +>foo : ["a", string, number] | ["b", string, boolean] +>index : number +>"a" : "a" + + foo; +>foo : ["a", string, number] | ["b", string, boolean] + } +} + diff --git a/tests/cases/compiler/avoidNarrowingUsingConstVariableFromBindingElementWithLiteralInitializer.ts b/tests/cases/compiler/avoidNarrowingUsingConstVariableFromBindingElementWithLiteralInitializer.ts new file mode 100644 index 00000000000..3fd22f46c8d --- /dev/null +++ b/tests/cases/compiler/avoidNarrowingUsingConstVariableFromBindingElementWithLiteralInitializer.ts @@ -0,0 +1,12 @@ +// @strict: true +// @noEmit: true + +declare const foo: ["a", string, number] | ["b", string, boolean]; + +export function test(arg: { index?: number }) { + const { index = 0 } = arg; + + if (foo[index] === "a") { + foo; + } +}