Skip to content

Commit 7d00bb0

Browse files
authored
Widening and non-widening computed enum types (#52542)
1 parent de2ab4a commit 7d00bb0

22 files changed

+980
-60
lines changed

src/compiler/checker.ts

Lines changed: 31 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,7 @@ import {
157157
entityNameToString,
158158
EnumDeclaration,
159159
EnumMember,
160+
EnumType,
160161
equateValues,
161162
escapeLeadingUnderscores,
162163
escapeString,
@@ -9660,8 +9661,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
96609661
const t = types[i];
96619662
flags |= t.flags;
96629663
if (!(t.flags & TypeFlags.Nullable)) {
9663-
if (t.flags & (TypeFlags.BooleanLiteral | TypeFlags.EnumLiteral)) {
9664-
const baseType = t.flags & TypeFlags.BooleanLiteral ? booleanType : getBaseTypeOfEnumLiteralType(t as LiteralType);
9664+
if (t.flags & (TypeFlags.BooleanLiteral | TypeFlags.EnumLike)) {
9665+
const baseType = t.flags & TypeFlags.BooleanLiteral ? booleanType : getBaseTypeOfEnumLikeType(t as LiteralType);
96659666
if (baseType.flags & TypeFlags.Union) {
96669667
const count = (baseType as UnionType).types.length;
96679668
if (i + count <= types.length && getRegularTypeOfLiteralType(types[i + count - 1]) === getRegularTypeOfLiteralType((baseType as UnionType).types[count - 1])) {
@@ -11906,8 +11907,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
1190611907
return links.declaredType;
1190711908
}
1190811909

11909-
function getBaseTypeOfEnumLiteralType(type: Type) {
11910-
return type.flags & TypeFlags.EnumLiteral && !(type.flags & TypeFlags.Union) ? getDeclaredTypeOfSymbol(getParentOfSymbol(type.symbol)!) : type;
11910+
function getBaseTypeOfEnumLikeType(type: Type) {
11911+
return type.flags & TypeFlags.EnumLike && type.symbol.flags & SymbolFlags.EnumMember ? getDeclaredTypeOfSymbol(getParentOfSymbol(type.symbol)!) : type;
1191111912
}
1191211913

1191311914
function getDeclaredTypeOfEnum(symbol: Symbol): Type {
@@ -11921,9 +11922,9 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
1192111922
if (hasBindableName(member)) {
1192211923
const memberSymbol = getSymbolOfDeclaration(member);
1192311924
const value = getEnumMemberValue(member);
11924-
const memberType = value !== undefined ?
11925-
getFreshTypeOfLiteralType(getEnumLiteralType(value, getSymbolId(symbol), memberSymbol)) :
11926-
createTypeWithSymbol(TypeFlags.Enum, memberSymbol);
11925+
const memberType = getFreshTypeOfLiteralType(value !== undefined ?
11926+
getEnumLiteralType(value, getSymbolId(symbol), memberSymbol) :
11927+
createComputedEnumType(memberSymbol));
1192711928
getSymbolLinks(memberSymbol).declaredType = memberType;
1192811929
memberTypeList.push(getRegularTypeOfLiteralType(memberType));
1192911930
}
@@ -11933,7 +11934,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
1193311934
}
1193411935
const enumType = memberTypeList.length ?
1193511936
getUnionType(memberTypeList, UnionReduction.Literal, symbol, /*aliasTypeArguments*/ undefined) :
11936-
createTypeWithSymbol(TypeFlags.Enum, symbol);
11937+
createComputedEnumType(symbol);
1193711938
if (enumType.flags & TypeFlags.Union) {
1193811939
enumType.flags |= TypeFlags.EnumLiteral;
1193911940
enumType.symbol = symbol;
@@ -11943,6 +11944,16 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
1194311944
return links.declaredType;
1194411945
}
1194511946

11947+
function createComputedEnumType(symbol: Symbol) {
11948+
const regularType = createTypeWithSymbol(TypeFlags.Enum, symbol) as EnumType;
11949+
const freshType = createTypeWithSymbol(TypeFlags.Enum, symbol) as EnumType;
11950+
regularType.regularType = regularType;
11951+
regularType.freshType = freshType;
11952+
freshType.regularType = regularType;
11953+
freshType.freshType = freshType;
11954+
return regularType;
11955+
}
11956+
1194611957
function getDeclaredTypeOfEnumMember(symbol: Symbol): Type {
1194711958
const links = getSymbolLinks(symbol);
1194811959
if (!links.declaredType) {
@@ -16201,7 +16212,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
1620116212
orderedRemoveItemAt(typeSet, 1);
1620216213
}
1620316214
}
16204-
if (includes & (TypeFlags.Literal | TypeFlags.UniqueESSymbol | TypeFlags.TemplateLiteral | TypeFlags.StringMapping) || includes & TypeFlags.Void && includes & TypeFlags.Undefined) {
16215+
if (includes & (TypeFlags.Enum | TypeFlags.Literal | TypeFlags.UniqueESSymbol | TypeFlags.TemplateLiteral | TypeFlags.StringMapping) || includes & TypeFlags.Void && includes & TypeFlags.Undefined) {
1620516216
removeRedundantLiteralTypes(typeSet, includes, !!(unionReduction & UnionReduction.Subtype));
1620616217
}
1620716218
if (includes & TypeFlags.StringLiteral && includes & TypeFlags.TemplateLiteral) {
@@ -18046,25 +18057,25 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
1804618057
}
1804718058

1804818059
function getFreshTypeOfLiteralType(type: Type): Type {
18049-
if (type.flags & TypeFlags.Literal) {
18050-
if (!(type as LiteralType).freshType) {
18060+
if (type.flags & TypeFlags.Freshable) {
18061+
if (!(type as FreshableType).freshType) {
1805118062
const freshType = createLiteralType(type.flags, (type as LiteralType).value, (type as LiteralType).symbol, type as LiteralType);
1805218063
freshType.freshType = freshType;
18053-
(type as LiteralType).freshType = freshType;
18064+
(type as FreshableType).freshType = freshType;
1805418065
}
18055-
return (type as LiteralType).freshType;
18066+
return (type as FreshableType).freshType;
1805618067
}
1805718068
return type;
1805818069
}
1805918070

1806018071
function getRegularTypeOfLiteralType(type: Type): Type {
18061-
return type.flags & TypeFlags.Literal ? (type as LiteralType).regularType :
18072+
return type.flags & TypeFlags.Freshable ? (type as FreshableType).regularType :
1806218073
type.flags & TypeFlags.Union ? ((type as UnionType).regularType || ((type as UnionType).regularType = mapType(type, getRegularTypeOfLiteralType) as UnionType)) :
1806318074
type;
1806418075
}
1806518076

1806618077
function isFreshLiteralType(type: Type) {
18067-
return !!(type.flags & TypeFlags.Literal) && (type as LiteralType).freshType === type;
18078+
return !!(type.flags & TypeFlags.Freshable) && (type as LiteralType).freshType === type;
1806818079
}
1806918080

1807018081
function getStringLiteralType(value: string): StringLiteralType {
@@ -22993,7 +23004,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
2299323004
}
2299423005

2299523006
function getBaseTypeOfLiteralType(type: Type): Type {
22996-
return type.flags & TypeFlags.EnumLiteral ? getBaseTypeOfEnumLiteralType(type as LiteralType) :
23007+
return type.flags & TypeFlags.EnumLike ? getBaseTypeOfEnumLikeType(type as LiteralType) :
2299723008
type.flags & (TypeFlags.StringLiteral | TypeFlags.TemplateLiteral | TypeFlags.StringMapping) ? stringType :
2299823009
type.flags & TypeFlags.NumberLiteral ? numberType :
2299923010
type.flags & TypeFlags.BigIntLiteral ? bigintType :
@@ -23008,7 +23019,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
2300823019
}
2300923020

2301023021
function getWidenedLiteralType(type: Type): Type {
23011-
return type.flags & TypeFlags.EnumLiteral && isFreshLiteralType(type) ? getBaseTypeOfEnumLiteralType(type as LiteralType) :
23022+
return type.flags & TypeFlags.EnumLike && isFreshLiteralType(type) ? getBaseTypeOfEnumLikeType(type as LiteralType) :
2301223023
type.flags & TypeFlags.StringLiteral && isFreshLiteralType(type) ? stringType :
2301323024
type.flags & TypeFlags.NumberLiteral && isFreshLiteralType(type) ? numberType :
2301423025
type.flags & TypeFlags.BigIntLiteral && isFreshLiteralType(type) ? bigintType :
@@ -25652,7 +25663,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
2565225663
}
2565325664
return true;
2565425665
}
25655-
if (source.flags & TypeFlags.EnumLiteral && getBaseTypeOfEnumLiteralType(source as LiteralType) === target) {
25666+
if (source.flags & TypeFlags.EnumLike && getBaseTypeOfEnumLikeType(source as LiteralType) === target) {
2565625667
return true;
2565725668
}
2565825669
return containsType(target.types, source);
@@ -45995,7 +46006,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
4599546006
}
4599646007

4599746008
function literalTypeToNode(type: FreshableType, enclosing: Node, tracker: SymbolTracker): Expression {
45998-
const enumResult = type.flags & TypeFlags.EnumLiteral ? nodeBuilder.symbolToExpression(type.symbol, SymbolFlags.Value, enclosing, /*flags*/ undefined, tracker)
46009+
const enumResult = type.flags & TypeFlags.EnumLike ? nodeBuilder.symbolToExpression(type.symbol, SymbolFlags.Value, enclosing, /*flags*/ undefined, tracker)
4599946010
: type === trueType ? factory.createTrue() : type === falseType && factory.createFalse();
4600046011
if (enumResult) return enumResult;
4600146012
const literalValue = (type as LiteralType).value;
@@ -47782,7 +47793,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
4778247793
function isSimpleLiteralEnumReference(expr: Expression) {
4778347794
if ((isPropertyAccessExpression(expr) || (isElementAccessExpression(expr) && isStringOrNumberLiteralExpression(expr.argumentExpression))) &&
4778447795
isEntityNameExpression(expr.expression)) {
47785-
return !!(checkExpressionCached(expr).flags & TypeFlags.EnumLiteral);
47796+
return !!(checkExpressionCached(expr).flags & TypeFlags.EnumLike);
4778647797
}
4778747798
}
4778847799

src/compiler/types.ts

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6043,7 +6043,8 @@ export const enum TypeFlags {
60436043
/** @internal */
60446044
Nullable = Undefined | Null,
60456045
Literal = StringLiteral | NumberLiteral | BigIntLiteral | BooleanLiteral,
6046-
Unit = Literal | UniqueESSymbol | Nullable,
6046+
Unit = Enum | Literal | UniqueESSymbol | Nullable,
6047+
Freshable = Enum | Literal,
60476048
StringOrNumberLiteral = StringLiteral | NumberLiteral,
60486049
/** @internal */
60496050
StringOrNumberLiteralOrUnique = StringLiteral | NumberLiteral | UniqueESSymbol,
@@ -6137,22 +6138,20 @@ export interface NullableType extends IntrinsicType {
61376138
objectFlags: ObjectFlags;
61386139
}
61396140

6140-
/** @internal */
6141-
export interface FreshableIntrinsicType extends IntrinsicType {
6142-
freshType: IntrinsicType; // Fresh version of type
6143-
regularType: IntrinsicType; // Regular version of type
6141+
export interface FreshableType extends Type {
6142+
freshType: FreshableType; // Fresh version of type
6143+
regularType: FreshableType; // Regular version of type
61446144
}
61456145

61466146
/** @internal */
6147-
export type FreshableType = LiteralType | FreshableIntrinsicType;
6147+
export interface FreshableIntrinsicType extends FreshableType, IntrinsicType {
6148+
}
61486149

61496150
// String literal types (TypeFlags.StringLiteral)
61506151
// Numeric literal types (TypeFlags.NumberLiteral)
61516152
// BigInt literal types (TypeFlags.BigIntLiteral)
6152-
export interface LiteralType extends Type {
6153+
export interface LiteralType extends FreshableType {
61536154
value: string | number | PseudoBigInt; // Value of literal
6154-
freshType: LiteralType; // Fresh version of type
6155-
regularType: LiteralType; // Regular version of type
61566155
}
61576156

61586157
// Unique symbol types (TypeFlags.UniqueESSymbol)
@@ -6174,7 +6173,7 @@ export interface BigIntLiteralType extends LiteralType {
61746173
}
61756174

61766175
// Enum types (TypeFlags.Enum)
6177-
export interface EnumType extends Type {
6176+
export interface EnumType extends FreshableType {
61786177
}
61796178

61806179
// Types included in TypeFlags.ObjectFlagsType have an objectFlags property. Some ObjectFlags

tests/baselines/reference/ambientDeclarations.types

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ declare enum E3 {
118118
>E3 : E3
119119

120120
A
121-
>A : E3
121+
>A : E3.A
122122
}
123123
declare module E3 {
124124
>E3 : typeof E3

tests/baselines/reference/ambientEnum1.types

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
>E2 : E2
1313

1414
x = 'foo'.length
15-
>x : E2
15+
>x : E2.x
1616
>'foo'.length : number
1717
>'foo' : "foo"
1818
>length : number

tests/baselines/reference/ambientErrors.types

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ declare enum E2 {
5555
>E2 : E2
5656

5757
x = 'foo'.length
58-
>x : E2
58+
>x : E2.x
5959
>'foo'.length : number
6060
>'foo' : "foo"
6161
>length : number

tests/baselines/reference/api/tsserverlibrary.d.ts

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6675,7 +6675,8 @@ declare namespace ts {
66756675
TemplateLiteral = 134217728,
66766676
StringMapping = 268435456,
66776677
Literal = 2944,
6678-
Unit = 109440,
6678+
Unit = 109472,
6679+
Freshable = 2976,
66796680
StringOrNumberLiteral = 384,
66806681
PossiblyFalsy = 117724,
66816682
StringLike = 402653316,
@@ -6727,10 +6728,12 @@ declare namespace ts {
67276728
isClass(): this is InterfaceType;
67286729
isIndexType(): this is IndexType;
67296730
}
6730-
interface LiteralType extends Type {
6731+
interface FreshableType extends Type {
6732+
freshType: FreshableType;
6733+
regularType: FreshableType;
6734+
}
6735+
interface LiteralType extends FreshableType {
67316736
value: string | number | PseudoBigInt;
6732-
freshType: LiteralType;
6733-
regularType: LiteralType;
67346737
}
67356738
interface UniqueESSymbolType extends Type {
67366739
symbol: Symbol;
@@ -6745,7 +6748,7 @@ declare namespace ts {
67456748
interface BigIntLiteralType extends LiteralType {
67466749
value: PseudoBigInt;
67476750
}
6748-
interface EnumType extends Type {
6751+
interface EnumType extends FreshableType {
67496752
}
67506753
enum ObjectFlags {
67516754
None = 0,

tests/baselines/reference/api/typescript.d.ts

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2700,7 +2700,8 @@ declare namespace ts {
27002700
TemplateLiteral = 134217728,
27012701
StringMapping = 268435456,
27022702
Literal = 2944,
2703-
Unit = 109440,
2703+
Unit = 109472,
2704+
Freshable = 2976,
27042705
StringOrNumberLiteral = 384,
27052706
PossiblyFalsy = 117724,
27062707
StringLike = 402653316,
@@ -2752,10 +2753,12 @@ declare namespace ts {
27522753
isClass(): this is InterfaceType;
27532754
isIndexType(): this is IndexType;
27542755
}
2755-
interface LiteralType extends Type {
2756+
interface FreshableType extends Type {
2757+
freshType: FreshableType;
2758+
regularType: FreshableType;
2759+
}
2760+
interface LiteralType extends FreshableType {
27562761
value: string | number | PseudoBigInt;
2757-
freshType: LiteralType;
2758-
regularType: LiteralType;
27592762
}
27602763
interface UniqueESSymbolType extends Type {
27612764
symbol: Symbol;
@@ -2770,7 +2773,7 @@ declare namespace ts {
27702773
interface BigIntLiteralType extends LiteralType {
27712774
value: PseudoBigInt;
27722775
}
2773-
interface EnumType extends Type {
2776+
interface EnumType extends FreshableType {
27742777
}
27752778
enum ObjectFlags {
27762779
None = 0,

0 commit comments

Comments
 (0)