diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 3a137ac66fe41..081130a048194 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -45494,8 +45494,17 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } if (isConstantVariable(symbol)) { const declaration = symbol.valueDeclaration; - if (declaration && isVariableDeclaration(declaration) && !declaration.type && declaration.initializer && (!location || declaration !== location && isBlockScopedNameDeclaredBeforeUse(declaration, location))) { - return evaluate(declaration.initializer, declaration); + if (declaration && isVariableDeclaration(declaration) && declaration.initializer && (!location || declaration !== location && isBlockScopedNameDeclaredBeforeUse(declaration, location))) { + if (!declaration.type) { + return evaluate(declaration.initializer, declaration); + } + const declaredType = getTypeOfSymbol(symbol); + const type = declaredType.flags & TypeFlags.Union ? + getAssignmentReducedType(declaredType as UnionType, getInitialType(declaration)) : + declaredType; + if (type.flags & TypeFlags.StringOrNumberLiteral) { + return (type as StringLiteralType | NumberLiteralType).value; + } } } } diff --git a/tests/baselines/reference/discriminantUsingEvaluatableTemplateExpression.symbols b/tests/baselines/reference/discriminantUsingEvaluatableTemplateExpression.symbols index 476ad7f878d25..7291a6f563333 100644 --- a/tests/baselines/reference/discriminantUsingEvaluatableTemplateExpression.symbols +++ b/tests/baselines/reference/discriminantUsingEvaluatableTemplateExpression.symbols @@ -37,3 +37,35 @@ foo({ }, }); +type E1 = { d: "main1-sub1"; cb: (x: string) => void }; +>E1 : Symbol(E1, Decl(discriminantUsingEvaluatableTemplateExpression.ts, 12, 3)) +>d : Symbol(d, Decl(discriminantUsingEvaluatableTemplateExpression.ts, 14, 11)) +>cb : Symbol(cb, Decl(discriminantUsingEvaluatableTemplateExpression.ts, 14, 28)) +>x : Symbol(x, Decl(discriminantUsingEvaluatableTemplateExpression.ts, 14, 34)) + +type E2 = { d: "main2-sub2"; cb: (x: number) => void }; +>E2 : Symbol(E2, Decl(discriminantUsingEvaluatableTemplateExpression.ts, 14, 55)) +>d : Symbol(d, Decl(discriminantUsingEvaluatableTemplateExpression.ts, 15, 11)) +>cb : Symbol(cb, Decl(discriminantUsingEvaluatableTemplateExpression.ts, 15, 28)) +>x : Symbol(x, Decl(discriminantUsingEvaluatableTemplateExpression.ts, 15, 34)) + +declare function bar(_: E1 | E2): void; +>bar : Symbol(bar, Decl(discriminantUsingEvaluatableTemplateExpression.ts, 15, 55)) +>_ : Symbol(_, Decl(discriminantUsingEvaluatableTemplateExpression.ts, 17, 21)) +>E1 : Symbol(E1, Decl(discriminantUsingEvaluatableTemplateExpression.ts, 12, 3)) +>E2 : Symbol(E2, Decl(discriminantUsingEvaluatableTemplateExpression.ts, 14, 55)) + +const someCategory = "main1"; +>someCategory : Symbol(someCategory, Decl(discriminantUsingEvaluatableTemplateExpression.ts, 19, 5)) + +const someSubcategory = "sub1"; +>someSubcategory : Symbol(someSubcategory, Decl(discriminantUsingEvaluatableTemplateExpression.ts, 20, 5)) + +bar({ d: `${someCategory}-${someSubcategory}`, cb: (x) => {} }); +>bar : Symbol(bar, Decl(discriminantUsingEvaluatableTemplateExpression.ts, 15, 55)) +>d : Symbol(d, Decl(discriminantUsingEvaluatableTemplateExpression.ts, 22, 5)) +>someCategory : Symbol(someCategory, Decl(discriminantUsingEvaluatableTemplateExpression.ts, 19, 5)) +>someSubcategory : Symbol(someSubcategory, Decl(discriminantUsingEvaluatableTemplateExpression.ts, 20, 5)) +>cb : Symbol(cb, Decl(discriminantUsingEvaluatableTemplateExpression.ts, 22, 46)) +>x : Symbol(x, Decl(discriminantUsingEvaluatableTemplateExpression.ts, 22, 52)) + diff --git a/tests/baselines/reference/discriminantUsingEvaluatableTemplateExpression.types b/tests/baselines/reference/discriminantUsingEvaluatableTemplateExpression.types index 08460e061b0a2..fb9a66f3f8aec 100644 --- a/tests/baselines/reference/discriminantUsingEvaluatableTemplateExpression.types +++ b/tests/baselines/reference/discriminantUsingEvaluatableTemplateExpression.types @@ -40,3 +40,39 @@ foo({ }, }); +type E1 = { d: "main1-sub1"; cb: (x: string) => void }; +>E1 : { d: "main1-sub1"; cb: (x: string) => void; } +>d : "main1-sub1" +>cb : (x: string) => void +>x : string + +type E2 = { d: "main2-sub2"; cb: (x: number) => void }; +>E2 : { d: "main2-sub2"; cb: (x: number) => void; } +>d : "main2-sub2" +>cb : (x: number) => void +>x : number + +declare function bar(_: E1 | E2): void; +>bar : (_: E1 | E2) => void +>_ : E1 | E2 + +const someCategory = "main1"; +>someCategory : "main1" +>"main1" : "main1" + +const someSubcategory = "sub1"; +>someSubcategory : "sub1" +>"sub1" : "sub1" + +bar({ d: `${someCategory}-${someSubcategory}`, cb: (x) => {} }); +>bar({ d: `${someCategory}-${someSubcategory}`, cb: (x) => {} }) : void +>bar : (_: E1 | E2) => void +>{ d: `${someCategory}-${someSubcategory}`, cb: (x) => {} } : { d: "main1-sub1"; cb: (x: string) => void; } +>d : "main1-sub1" +>`${someCategory}-${someSubcategory}` : "main1-sub1" +>someCategory : "main1" +>someSubcategory : "sub1" +>cb : (x: string) => void +>(x) => {} : (x: string) => void +>x : string + diff --git a/tests/baselines/reference/discriminatedUnionTypesOverlappingDiscriminants.symbols b/tests/baselines/reference/discriminatedUnionTypesOverlappingDiscriminants.symbols new file mode 100644 index 0000000000000..23620d667fff1 --- /dev/null +++ b/tests/baselines/reference/discriminatedUnionTypesOverlappingDiscriminants.symbols @@ -0,0 +1,160 @@ +//// [tests/cases/conformance/types/union/discriminatedUnionTypesOverlappingDiscriminants.ts] //// + +=== discriminatedUnionTypesOverlappingDiscriminants.ts === +// https://github.com/microsoft/TypeScript/issues/57231 + +type Food = "apple" | "orange"; +>Food : Symbol(Food, Decl(discriminatedUnionTypesOverlappingDiscriminants.ts, 0, 0)) + +type Vegetable = "spinach" | "carrot"; +>Vegetable : Symbol(Vegetable, Decl(discriminatedUnionTypesOverlappingDiscriminants.ts, 2, 31)) + +type Other = "milk" | "water"; +>Other : Symbol(Other, Decl(discriminatedUnionTypesOverlappingDiscriminants.ts, 3, 38)) + +type Custom = "air" | "soil"; +>Custom : Symbol(Custom, Decl(discriminatedUnionTypesOverlappingDiscriminants.ts, 4, 30)) + +type Target = +>Target : Symbol(Target, Decl(discriminatedUnionTypesOverlappingDiscriminants.ts, 5, 29)) + + | { + audience: "earth"; +>audience : Symbol(audience, Decl(discriminatedUnionTypesOverlappingDiscriminants.ts, 8, 5)) + + meal: +>meal : Symbol(meal, Decl(discriminatedUnionTypesOverlappingDiscriminants.ts, 9, 24)) + + | Custom +>Custom : Symbol(Custom, Decl(discriminatedUnionTypesOverlappingDiscriminants.ts, 4, 30)) + + | `fruit_${Food}` +>Food : Symbol(Food, Decl(discriminatedUnionTypesOverlappingDiscriminants.ts, 0, 0)) + + | `vegetable_${Vegetable}` +>Vegetable : Symbol(Vegetable, Decl(discriminatedUnionTypesOverlappingDiscriminants.ts, 2, 31)) + + | `other_${Other}`; +>Other : Symbol(Other, Decl(discriminatedUnionTypesOverlappingDiscriminants.ts, 3, 38)) + } + | { + audience: "mars" | "jupiter"; +>audience : Symbol(audience, Decl(discriminatedUnionTypesOverlappingDiscriminants.ts, 16, 5)) + + meal: string; +>meal : Symbol(meal, Decl(discriminatedUnionTypesOverlappingDiscriminants.ts, 17, 35)) + + }; + +const target1: Target = { +>target1 : Symbol(target1, Decl(discriminatedUnionTypesOverlappingDiscriminants.ts, 21, 5)) +>Target : Symbol(Target, Decl(discriminatedUnionTypesOverlappingDiscriminants.ts, 5, 29)) + + audience: "earth", +>audience : Symbol(audience, Decl(discriminatedUnionTypesOverlappingDiscriminants.ts, 21, 25)) + + meal: `vegetable_carrot`, +>meal : Symbol(meal, Decl(discriminatedUnionTypesOverlappingDiscriminants.ts, 22, 20)) + +}; + +const target2: Target = { +>target2 : Symbol(target2, Decl(discriminatedUnionTypesOverlappingDiscriminants.ts, 26, 5)) +>Target : Symbol(Target, Decl(discriminatedUnionTypesOverlappingDiscriminants.ts, 5, 29)) + + meal: `vegetable_carrot`, +>meal : Symbol(meal, Decl(discriminatedUnionTypesOverlappingDiscriminants.ts, 26, 25)) + + audience: "earth", +>audience : Symbol(audience, Decl(discriminatedUnionTypesOverlappingDiscriminants.ts, 27, 27)) + +}; + +const typedVegetableWithInitializer: Vegetable = 'carrot'; +>typedVegetableWithInitializer : Symbol(typedVegetableWithInitializer, Decl(discriminatedUnionTypesOverlappingDiscriminants.ts, 31, 5)) +>Vegetable : Symbol(Vegetable, Decl(discriminatedUnionTypesOverlappingDiscriminants.ts, 2, 31)) + +const target3: Target = { +>target3 : Symbol(target3, Decl(discriminatedUnionTypesOverlappingDiscriminants.ts, 33, 5)) +>Target : Symbol(Target, Decl(discriminatedUnionTypesOverlappingDiscriminants.ts, 5, 29)) + + audience: "earth", +>audience : Symbol(audience, Decl(discriminatedUnionTypesOverlappingDiscriminants.ts, 33, 25)) + + meal: `vegetable_${typedVegetableWithInitializer}`, +>meal : Symbol(meal, Decl(discriminatedUnionTypesOverlappingDiscriminants.ts, 34, 20)) +>typedVegetableWithInitializer : Symbol(typedVegetableWithInitializer, Decl(discriminatedUnionTypesOverlappingDiscriminants.ts, 31, 5)) + +}; + +const target4: Target = { +>target4 : Symbol(target4, Decl(discriminatedUnionTypesOverlappingDiscriminants.ts, 38, 5)) +>Target : Symbol(Target, Decl(discriminatedUnionTypesOverlappingDiscriminants.ts, 5, 29)) + + meal: `vegetable_${typedVegetableWithInitializer}`, +>meal : Symbol(meal, Decl(discriminatedUnionTypesOverlappingDiscriminants.ts, 38, 25)) +>typedVegetableWithInitializer : Symbol(typedVegetableWithInitializer, Decl(discriminatedUnionTypesOverlappingDiscriminants.ts, 31, 5)) + + audience: "earth", +>audience : Symbol(audience, Decl(discriminatedUnionTypesOverlappingDiscriminants.ts, 39, 53)) + +}; + +const typedCarrotWithInitializer: "carrot" = 'carrot'; +>typedCarrotWithInitializer : Symbol(typedCarrotWithInitializer, Decl(discriminatedUnionTypesOverlappingDiscriminants.ts, 43, 5)) + +const target5: Target = { +>target5 : Symbol(target5, Decl(discriminatedUnionTypesOverlappingDiscriminants.ts, 45, 5)) +>Target : Symbol(Target, Decl(discriminatedUnionTypesOverlappingDiscriminants.ts, 5, 29)) + + audience: "earth", +>audience : Symbol(audience, Decl(discriminatedUnionTypesOverlappingDiscriminants.ts, 45, 25)) + + meal: `vegetable_${typedCarrotWithInitializer}`, +>meal : Symbol(meal, Decl(discriminatedUnionTypesOverlappingDiscriminants.ts, 46, 20)) +>typedCarrotWithInitializer : Symbol(typedCarrotWithInitializer, Decl(discriminatedUnionTypesOverlappingDiscriminants.ts, 43, 5)) + +}; + +const target6: Target = { +>target6 : Symbol(target6, Decl(discriminatedUnionTypesOverlappingDiscriminants.ts, 50, 5)) +>Target : Symbol(Target, Decl(discriminatedUnionTypesOverlappingDiscriminants.ts, 5, 29)) + + meal: `vegetable_${typedCarrotWithInitializer}`, +>meal : Symbol(meal, Decl(discriminatedUnionTypesOverlappingDiscriminants.ts, 50, 25)) +>typedCarrotWithInitializer : Symbol(typedCarrotWithInitializer, Decl(discriminatedUnionTypesOverlappingDiscriminants.ts, 43, 5)) + + audience: "earth", +>audience : Symbol(audience, Decl(discriminatedUnionTypesOverlappingDiscriminants.ts, 51, 50)) + +}; + +const carrotInitializer = 'carrot'; +>carrotInitializer : Symbol(carrotInitializer, Decl(discriminatedUnionTypesOverlappingDiscriminants.ts, 55, 5)) + +const target7: Target = { +>target7 : Symbol(target7, Decl(discriminatedUnionTypesOverlappingDiscriminants.ts, 57, 5)) +>Target : Symbol(Target, Decl(discriminatedUnionTypesOverlappingDiscriminants.ts, 5, 29)) + + audience: "earth", +>audience : Symbol(audience, Decl(discriminatedUnionTypesOverlappingDiscriminants.ts, 57, 25)) + + meal: `vegetable_${carrotInitializer}`, +>meal : Symbol(meal, Decl(discriminatedUnionTypesOverlappingDiscriminants.ts, 58, 20)) +>carrotInitializer : Symbol(carrotInitializer, Decl(discriminatedUnionTypesOverlappingDiscriminants.ts, 55, 5)) + +}; + +const target8: Target = { +>target8 : Symbol(target8, Decl(discriminatedUnionTypesOverlappingDiscriminants.ts, 62, 5)) +>Target : Symbol(Target, Decl(discriminatedUnionTypesOverlappingDiscriminants.ts, 5, 29)) + + meal: `vegetable_${carrotInitializer}`, +>meal : Symbol(meal, Decl(discriminatedUnionTypesOverlappingDiscriminants.ts, 62, 25)) +>carrotInitializer : Symbol(carrotInitializer, Decl(discriminatedUnionTypesOverlappingDiscriminants.ts, 55, 5)) + + audience: "earth", +>audience : Symbol(audience, Decl(discriminatedUnionTypesOverlappingDiscriminants.ts, 63, 41)) + +}; + diff --git a/tests/baselines/reference/discriminatedUnionTypesOverlappingDiscriminants.types b/tests/baselines/reference/discriminatedUnionTypesOverlappingDiscriminants.types new file mode 100644 index 0000000000000..90d3284844064 --- /dev/null +++ b/tests/baselines/reference/discriminatedUnionTypesOverlappingDiscriminants.types @@ -0,0 +1,171 @@ +//// [tests/cases/conformance/types/union/discriminatedUnionTypesOverlappingDiscriminants.ts] //// + +=== discriminatedUnionTypesOverlappingDiscriminants.ts === +// https://github.com/microsoft/TypeScript/issues/57231 + +type Food = "apple" | "orange"; +>Food : "apple" | "orange" + +type Vegetable = "spinach" | "carrot"; +>Vegetable : "spinach" | "carrot" + +type Other = "milk" | "water"; +>Other : "milk" | "water" + +type Custom = "air" | "soil"; +>Custom : "air" | "soil" + +type Target = +>Target : { audience: "earth"; meal: Custom | `fruit_${Food}` | `vegetable_${Vegetable}` | `other_${Other}`; } | { audience: "mars" | "jupiter"; meal: string; } + + | { + audience: "earth"; +>audience : "earth" + + meal: +>meal : Custom | "fruit_apple" | "fruit_orange" | "vegetable_spinach" | "vegetable_carrot" | "other_milk" | "other_water" + + | Custom + | `fruit_${Food}` + | `vegetable_${Vegetable}` + | `other_${Other}`; + } + | { + audience: "mars" | "jupiter"; +>audience : "mars" | "jupiter" + + meal: string; +>meal : string + + }; + +const target1: Target = { +>target1 : Target +>{ audience: "earth", meal: `vegetable_carrot`,} : { audience: "earth"; meal: "vegetable_carrot"; } + + audience: "earth", +>audience : "earth" +>"earth" : "earth" + + meal: `vegetable_carrot`, +>meal : "vegetable_carrot" +>`vegetable_carrot` : "vegetable_carrot" + +}; + +const target2: Target = { +>target2 : Target +>{ meal: `vegetable_carrot`, audience: "earth",} : { meal: "vegetable_carrot"; audience: "earth"; } + + meal: `vegetable_carrot`, +>meal : "vegetable_carrot" +>`vegetable_carrot` : "vegetable_carrot" + + audience: "earth", +>audience : "earth" +>"earth" : "earth" + +}; + +const typedVegetableWithInitializer: Vegetable = 'carrot'; +>typedVegetableWithInitializer : Vegetable +>'carrot' : "carrot" + +const target3: Target = { +>target3 : Target +>{ audience: "earth", meal: `vegetable_${typedVegetableWithInitializer}`,} : { audience: "earth"; meal: "vegetable_carrot"; } + + audience: "earth", +>audience : "earth" +>"earth" : "earth" + + meal: `vegetable_${typedVegetableWithInitializer}`, +>meal : "vegetable_carrot" +>`vegetable_${typedVegetableWithInitializer}` : "vegetable_carrot" +>typedVegetableWithInitializer : "carrot" + +}; + +const target4: Target = { +>target4 : Target +>{ meal: `vegetable_${typedVegetableWithInitializer}`, audience: "earth",} : { meal: "vegetable_carrot"; audience: "earth"; } + + meal: `vegetable_${typedVegetableWithInitializer}`, +>meal : "vegetable_carrot" +>`vegetable_${typedVegetableWithInitializer}` : "vegetable_carrot" +>typedVegetableWithInitializer : "carrot" + + audience: "earth", +>audience : "earth" +>"earth" : "earth" + +}; + +const typedCarrotWithInitializer: "carrot" = 'carrot'; +>typedCarrotWithInitializer : "carrot" +>'carrot' : "carrot" + +const target5: Target = { +>target5 : Target +>{ audience: "earth", meal: `vegetable_${typedCarrotWithInitializer}`,} : { audience: "earth"; meal: "vegetable_carrot"; } + + audience: "earth", +>audience : "earth" +>"earth" : "earth" + + meal: `vegetable_${typedCarrotWithInitializer}`, +>meal : "vegetable_carrot" +>`vegetable_${typedCarrotWithInitializer}` : "vegetable_carrot" +>typedCarrotWithInitializer : "carrot" + +}; + +const target6: Target = { +>target6 : Target +>{ meal: `vegetable_${typedCarrotWithInitializer}`, audience: "earth",} : { meal: "vegetable_carrot"; audience: "earth"; } + + meal: `vegetable_${typedCarrotWithInitializer}`, +>meal : "vegetable_carrot" +>`vegetable_${typedCarrotWithInitializer}` : "vegetable_carrot" +>typedCarrotWithInitializer : "carrot" + + audience: "earth", +>audience : "earth" +>"earth" : "earth" + +}; + +const carrotInitializer = 'carrot'; +>carrotInitializer : "carrot" +>'carrot' : "carrot" + +const target7: Target = { +>target7 : Target +>{ audience: "earth", meal: `vegetable_${carrotInitializer}`,} : { audience: "earth"; meal: "vegetable_carrot"; } + + audience: "earth", +>audience : "earth" +>"earth" : "earth" + + meal: `vegetable_${carrotInitializer}`, +>meal : "vegetable_carrot" +>`vegetable_${carrotInitializer}` : "vegetable_carrot" +>carrotInitializer : "carrot" + +}; + +const target8: Target = { +>target8 : Target +>{ meal: `vegetable_${carrotInitializer}`, audience: "earth",} : { meal: "vegetable_carrot"; audience: "earth"; } + + meal: `vegetable_${carrotInitializer}`, +>meal : "vegetable_carrot" +>`vegetable_${carrotInitializer}` : "vegetable_carrot" +>carrotInitializer : "carrot" + + audience: "earth", +>audience : "earth" +>"earth" : "earth" + +}; + diff --git a/tests/cases/compiler/discriminantUsingEvaluatableTemplateExpression.ts b/tests/cases/compiler/discriminantUsingEvaluatableTemplateExpression.ts index 17c0be879ebc6..87fb8048dcfd3 100644 --- a/tests/cases/compiler/discriminantUsingEvaluatableTemplateExpression.ts +++ b/tests/cases/compiler/discriminantUsingEvaluatableTemplateExpression.ts @@ -14,3 +14,13 @@ foo({ x; // string }, }); + +type E1 = { d: "main1-sub1"; cb: (x: string) => void }; +type E2 = { d: "main2-sub2"; cb: (x: number) => void }; + +declare function bar(_: E1 | E2): void; + +const someCategory = "main1"; +const someSubcategory = "sub1"; + +bar({ d: `${someCategory}-${someSubcategory}`, cb: (x) => {} }); diff --git a/tests/cases/conformance/types/union/discriminatedUnionTypesOverlappingDiscriminants.ts b/tests/cases/conformance/types/union/discriminatedUnionTypesOverlappingDiscriminants.ts new file mode 100644 index 0000000000000..ad0ceb715c8cc --- /dev/null +++ b/tests/cases/conformance/types/union/discriminatedUnionTypesOverlappingDiscriminants.ts @@ -0,0 +1,69 @@ +// @strict: true +// @noEmit: true + +// https://github.com/microsoft/TypeScript/issues/57231 + +type Food = "apple" | "orange"; +type Vegetable = "spinach" | "carrot"; +type Other = "milk" | "water"; +type Custom = "air" | "soil"; + +type Target = + | { + audience: "earth"; + meal: + | Custom + | `fruit_${Food}` + | `vegetable_${Vegetable}` + | `other_${Other}`; + } + | { + audience: "mars" | "jupiter"; + meal: string; + }; + +const target1: Target = { + audience: "earth", + meal: `vegetable_carrot`, +}; + +const target2: Target = { + meal: `vegetable_carrot`, + audience: "earth", +}; + +const typedVegetableWithInitializer: Vegetable = 'carrot'; + +const target3: Target = { + audience: "earth", + meal: `vegetable_${typedVegetableWithInitializer}`, +}; + +const target4: Target = { + meal: `vegetable_${typedVegetableWithInitializer}`, + audience: "earth", +}; + +const typedCarrotWithInitializer: "carrot" = 'carrot'; + +const target5: Target = { + audience: "earth", + meal: `vegetable_${typedCarrotWithInitializer}`, +}; + +const target6: Target = { + meal: `vegetable_${typedCarrotWithInitializer}`, + audience: "earth", +}; + +const carrotInitializer = 'carrot'; + +const target7: Target = { + audience: "earth", + meal: `vegetable_${carrotInitializer}`, +}; + +const target8: Target = { + meal: `vegetable_${carrotInitializer}`, + audience: "earth", +};