Skip to content

Evaluate expressions with annotated constant variables #57236

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 11 additions & 2 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that the initializer is still required for all of this to work here. I'm not sure what's the preferred way of handling potential errors in situations like this:

const str: 'foo' = 'bar'
const another = `_${str}` // "_foo" or "_bar" ?

I think the argument can be made both ways. I'm not sure if this matters all that much when the program errors anyway - but perhaps using the evaluated initializer in this situation would be slightly more consistent. After all, the initializer is still required for this to work - so the evaluation happens on the grounds that the expression that can be evaluated is there. It's not like this can be used as part of the evaluation: declare const str: 'foo';

It's also worth noting that this code here should be expanded with support for enum members.

}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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))

Original file line number Diff line number Diff line change
Expand Up @@ -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

Original file line number Diff line number Diff line change
@@ -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))

};

Loading