Skip to content

Commit 9223d1e

Browse files
Simplify declaration emit for literal types.
1 parent 353ccb7 commit 9223d1e

33 files changed

+676
-532
lines changed

src/compiler/checker.ts

Lines changed: 4 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -48324,8 +48324,11 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
4832448324
// Get type of the symbol if this is the valid symbol otherwise get type at location
4832548325
const symbol = getSymbolOfDeclaration(declaration);
4832648326
let type = symbol && !(symbol.flags & (SymbolFlags.TypeLiteral | SymbolFlags.Signature))
48327-
? getWidenedLiteralType(getTypeOfSymbol(symbol))
48327+
? getTypeOfSymbol(symbol)
4832848328
: errorType;
48329+
if (!isReadonlySymbol(symbol)) {
48330+
type = getWidenedLiteralType(type);
48331+
}
4832948332
if (
4833048333
type.flags & TypeFlags.UniqueESSymbol &&
4833148334
type.symbol === symbol
@@ -48455,29 +48458,6 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
4845548458
return undefined;
4845648459
}
4845748460

48458-
function isLiteralConstDeclaration(node: VariableDeclaration | PropertyDeclaration | PropertySignature | ParameterDeclaration): boolean {
48459-
if (isDeclarationReadonly(node) || isVariableDeclaration(node) && isVarConstLike(node)) {
48460-
return isFreshLiteralType(getTypeOfSymbol(getSymbolOfDeclaration(node)));
48461-
}
48462-
return false;
48463-
}
48464-
48465-
function literalTypeToNode(type: FreshableType, enclosing: Node, tracker: SymbolTracker): Expression {
48466-
const enumResult = type.flags & TypeFlags.EnumLike ? nodeBuilder.symbolToExpression(type.symbol, SymbolFlags.Value, enclosing, /*flags*/ undefined, tracker)
48467-
: type === trueType ? factory.createTrue() : type === falseType && factory.createFalse();
48468-
if (enumResult) return enumResult;
48469-
const literalValue = (type as LiteralType).value;
48470-
return typeof literalValue === "object" ? factory.createBigIntLiteral(literalValue) :
48471-
typeof literalValue === "string" ? factory.createStringLiteral(literalValue) :
48472-
literalValue < 0 ? factory.createPrefixUnaryExpression(SyntaxKind.MinusToken, factory.createNumericLiteral(-literalValue)) :
48473-
factory.createNumericLiteral(literalValue);
48474-
}
48475-
48476-
function createLiteralConstValue(node: VariableDeclaration | PropertyDeclaration | PropertySignature | ParameterDeclaration, tracker: SymbolTracker) {
48477-
const type = getTypeOfSymbol(getSymbolOfDeclaration(node));
48478-
return literalTypeToNode(type as FreshableType, node, tracker);
48479-
}
48480-
4848148461
function getJsxFactoryEntity(location: Node): EntityName | undefined {
4848248462
return location ? (getJsxNamespace(location), (getSourceFileOfNode(location).localJsxFactory || _jsxFactoryEntity)) : _jsxFactoryEntity;
4848348463
}
@@ -48553,7 +48533,6 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
4855348533
createTypeOfDeclaration,
4855448534
createReturnTypeOfSignatureDeclaration,
4855548535
createTypeOfExpression,
48556-
createLiteralConstValue,
4855748536
isSymbolAccessible,
4855848537
isEntityNameVisible,
4855948538
getConstantValue: nodeIn => {
@@ -48573,7 +48552,6 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
4857348552
},
4857448553
getTypeReferenceDirectivesForEntityName,
4857548554
getTypeReferenceDirectivesForSymbol,
48576-
isLiteralConstDeclaration,
4857748555
isLateBound: (nodeIn: Declaration): nodeIn is LateBoundDeclaration => {
4857848556
const node = getParseTreeNode(nodeIn, isDeclaration);
4857948557
const symbol = node && getSymbolOfDeclaration(node);

src/compiler/emitter.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1097,7 +1097,6 @@ export const notImplementedResolver: EmitResolver = {
10971097
createTypeOfDeclaration: notImplemented,
10981098
createReturnTypeOfSignatureDeclaration: notImplemented,
10991099
createTypeOfExpression: notImplemented,
1100-
createLiteralConstValue: notImplemented,
11011100
isSymbolAccessible: notImplemented,
11021101
isEntityNameVisible: notImplemented,
11031102
// Returns the constant value this property access resolves to: notImplemented, or 'undefined' for a non-constant
@@ -1111,7 +1110,6 @@ export const notImplementedResolver: EmitResolver = {
11111110
getExternalModuleFileFromDeclaration: notImplemented,
11121111
getTypeReferenceDirectivesForEntityName: notImplemented,
11131112
getTypeReferenceDirectivesForSymbol: notImplemented,
1114-
isLiteralConstDeclaration: notImplemented,
11151113
getJsxFactoryEntity: notImplemented,
11161114
getJsxFragmentFactoryEntity: notImplemented,
11171115
getAllAccessorDeclarations: notImplemented,

src/compiler/transformers/declarations.ts

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import {
1414
canHaveModifiers,
1515
canProduceDiagnostics,
1616
ClassDeclaration,
17+
clonePrimitiveLiteralValue,
1718
compact,
1819
concatenate,
1920
ConditionalTypeNode,
@@ -97,6 +98,7 @@ import {
9798
isClassElement,
9899
isComputedPropertyName,
99100
isDeclaration,
101+
isDeclarationReadonly,
100102
isEntityName,
101103
isEntityNameExpression,
102104
isExpandoPropertyDeclaration,
@@ -126,6 +128,7 @@ import {
126128
isModifier,
127129
isModuleDeclaration,
128130
isOmittedExpression,
131+
isPrimitiveLiteralValue,
129132
isPrivateIdentifier,
130133
isSemicolonClassElement,
131134
isSetAccessorDeclaration,
@@ -143,6 +146,7 @@ import {
143146
isTypeParameterDeclaration,
144147
isTypeQueryNode,
145148
isVarAwaitUsing,
149+
isVarConstLike,
146150
isVariableDeclaration,
147151
isVarUsing,
148152
LateBoundDeclaration,
@@ -174,6 +178,7 @@ import {
174178
parseNodeFactory,
175179
pathContainsNodeModules,
176180
pathIsRelative,
181+
PrimitiveLiteral,
177182
PropertyDeclaration,
178183
PropertySignature,
179184
pushIfUnique,
@@ -692,13 +697,16 @@ export function transformDeclarations(context: TransformationContext) {
692697
return newParam;
693698
}
694699

695-
function shouldPrintWithInitializer(node: Node) {
696-
return canHaveLiteralInitializer(node) && resolver.isLiteralConstDeclaration(getParseTreeNode(node) as CanHaveLiteralInitializer); // TODO: Make safe
700+
function shouldPrintWithInitializer(node: Node): node is Node & { initializer: PrimitiveLiteral; } {
701+
if (canHaveLiteralInitializer(node) && node.initializer && !node.type && isPrimitiveLiteralValue(node.initializer)) {
702+
return isDeclarationReadonly(node) || isVariableDeclaration(node) && isVarConstLike(node);
703+
}
704+
return false;
697705
}
698706

699707
function ensureNoInitializer(node: CanHaveLiteralInitializer) {
700708
if (shouldPrintWithInitializer(node)) {
701-
return resolver.createLiteralConstValue(getParseTreeNode(node) as CanHaveLiteralInitializer, symbolTracker); // TODO: Make safe
709+
return clonePrimitiveLiteralValue(node.initializer);
702710
}
703711
return undefined;
704712
}
@@ -1937,7 +1945,7 @@ function getTypeAnnotationFromAccessor(accessor: AccessorDeclaration): TypeNode
19371945
}
19381946

19391947
type CanHaveLiteralInitializer = VariableDeclaration | PropertyDeclaration | PropertySignature | ParameterDeclaration;
1940-
function canHaveLiteralInitializer(node: Node): boolean {
1948+
function canHaveLiteralInitializer(node: Node): node is CanHaveLiteralInitializer {
19411949
switch (node.kind) {
19421950
case SyntaxKind.PropertyDeclaration:
19431951
case SyntaxKind.PropertySignature:

src/compiler/types.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1376,6 +1376,15 @@ export type HasIllegalModifiers =
13761376
| MissingDeclaration
13771377
| NamespaceExportDeclaration;
13781378

1379+
/** @internal */
1380+
export type PrimitiveLiteral =
1381+
| BooleanLiteral
1382+
| NumericLiteral
1383+
| StringLiteral
1384+
| NoSubstitutionTemplateLiteral
1385+
| BigIntLiteral
1386+
| PrefixUnaryExpression;
1387+
13791388
/**
13801389
* Declarations that can contain other declarations. Corresponds with `ContainerFlags.IsContainer` in binder.ts.
13811390
*
@@ -5609,7 +5618,6 @@ export interface EmitResolver {
56095618
createTypeOfDeclaration(declaration: AccessorDeclaration | VariableLikeDeclaration | PropertyAccessExpression | ElementAccessExpression | BinaryExpression, enclosingDeclaration: Node, flags: NodeBuilderFlags, tracker: SymbolTracker, addUndefined?: boolean): TypeNode | undefined;
56105619
createReturnTypeOfSignatureDeclaration(signatureDeclaration: SignatureDeclaration, enclosingDeclaration: Node, flags: NodeBuilderFlags, tracker: SymbolTracker): TypeNode | undefined;
56115620
createTypeOfExpression(expr: Expression, enclosingDeclaration: Node, flags: NodeBuilderFlags, tracker: SymbolTracker): TypeNode | undefined;
5612-
createLiteralConstValue(node: VariableDeclaration | PropertyDeclaration | PropertySignature | ParameterDeclaration, tracker: SymbolTracker): Expression;
56135621
isSymbolAccessible(symbol: Symbol, enclosingDeclaration: Node | undefined, meaning: SymbolFlags | undefined, shouldComputeAliasToMarkVisible: boolean): SymbolAccessibilityResult;
56145622
isEntityNameVisible(entityName: EntityNameOrEntityNameExpression, enclosingDeclaration: Node): SymbolVisibilityResult;
56155623
// Returns the constant value this property access resolves to, or 'undefined' for a non-constant
@@ -5623,7 +5631,6 @@ export interface EmitResolver {
56235631
getExternalModuleFileFromDeclaration(declaration: ImportEqualsDeclaration | ImportDeclaration | ExportDeclaration | ModuleDeclaration | ImportTypeNode | ImportCall): SourceFile | undefined;
56245632
getTypeReferenceDirectivesForEntityName(name: EntityNameOrEntityNameExpression): [specifier: string, mode: ResolutionMode][] | undefined;
56255633
getTypeReferenceDirectivesForSymbol(symbol: Symbol, meaning?: SymbolFlags): [specifier: string, mode: ResolutionMode][] | undefined;
5626-
isLiteralConstDeclaration(node: VariableDeclaration | PropertyDeclaration | PropertySignature | ParameterDeclaration): boolean;
56275634
getJsxFactoryEntity(location?: Node): EntityName | undefined;
56285635
getJsxFragmentFactoryEntity(location?: Node): EntityName | undefined;
56295636
getAllAccessorDeclarations(declaration: AccessorDeclaration): AllAccessorDeclarations;

src/compiler/utilities.ts

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -444,6 +444,7 @@ import {
444444
Pattern,
445445
PostfixUnaryExpression,
446446
PrefixUnaryExpression,
447+
PrimitiveLiteral,
447448
PrinterOptions,
448449
PrintHandlers,
449450
PrivateIdentifier,
@@ -2343,6 +2344,14 @@ export function isLet(node: Node): boolean {
23432344
return (getCombinedNodeFlags(node) & NodeFlags.BlockScoped) === NodeFlags.Let;
23442345
}
23452346

2347+
/** @internal */
2348+
export function isVarConstLike(node: VariableDeclaration | VariableDeclarationList) {
2349+
const blockScopeKind = getCombinedNodeFlags(node) & NodeFlags.BlockScoped;
2350+
return blockScopeKind === NodeFlags.Const ||
2351+
blockScopeKind === NodeFlags.Using ||
2352+
blockScopeKind === NodeFlags.AwaitUsing;
2353+
}
2354+
23462355
/** @internal */
23472356
export function isSuperCall(n: Node): n is SuperCall {
23482357
return n.kind === SyntaxKind.CallExpression && (n as CallExpression).expression.kind === SyntaxKind.SuperKeyword;
@@ -10639,3 +10648,66 @@ export function replaceFirstStar(s: string, replacement: string): string {
1063910648
export function getNameFromImportAttribute(node: ImportAttribute) {
1064010649
return isIdentifier(node.name) ? node.name.escapedText : escapeLeadingUnderscores(node.name.text);
1064110650
}
10651+
10652+
/** @internal */
10653+
export function isPrimitiveLiteralValue(node: Expression, includeBigInt = true): node is PrimitiveLiteral {
10654+
Debug.type<PrimitiveLiteral>(node);
10655+
switch (node.kind) {
10656+
case SyntaxKind.TrueKeyword:
10657+
case SyntaxKind.FalseKeyword:
10658+
case SyntaxKind.NumericLiteral:
10659+
case SyntaxKind.StringLiteral:
10660+
case SyntaxKind.NoSubstitutionTemplateLiteral:
10661+
return true;
10662+
case SyntaxKind.BigIntLiteral:
10663+
return includeBigInt;
10664+
case SyntaxKind.PrefixUnaryExpression:
10665+
if (node.operator === SyntaxKind.MinusToken) {
10666+
return isNumericLiteral(node.operand) || (includeBigInt && isBigIntLiteral(node.operand));
10667+
}
10668+
if (node.operator === SyntaxKind.PlusToken) {
10669+
return isNumericLiteral(node.operand);
10670+
}
10671+
return false;
10672+
default:
10673+
assertType<never>(node);
10674+
return false;
10675+
}
10676+
}
10677+
10678+
/**
10679+
* @internal
10680+
*
10681+
* Clone literal value while normalizing it (converts octals/hex to base 10, uses double quotes strings)
10682+
*/
10683+
export function clonePrimitiveLiteralValue<T extends PrimitiveLiteral>(node: T): T;
10684+
export function clonePrimitiveLiteralValue(node: PrimitiveLiteral): PrimitiveLiteral {
10685+
switch (node.kind) {
10686+
case SyntaxKind.NumericLiteral:
10687+
return factory.createNumericLiteral(node.text);
10688+
case SyntaxKind.BigIntLiteral:
10689+
return factory.createBigIntLiteral({ negative: false, base10Value: parsePseudoBigInt(node.text) });
10690+
case SyntaxKind.StringLiteral:
10691+
case SyntaxKind.NoSubstitutionTemplateLiteral:
10692+
return factory.createStringLiteral(node.text);
10693+
case SyntaxKind.FalseKeyword:
10694+
return factory.createFalse();
10695+
case SyntaxKind.TrueKeyword:
10696+
return factory.createTrue();
10697+
case SyntaxKind.PrefixUnaryExpression:
10698+
Debug.assert(isNumericLiteral(node.operand) || isBigIntLiteral(node.operand));
10699+
if (node.operator === SyntaxKind.PlusToken) {
10700+
return clonePrimitiveLiteralValue(node.operand);
10701+
}
10702+
else if (node.operator === SyntaxKind.MinusToken) {
10703+
return factory.createPrefixUnaryExpression(
10704+
node.operator,
10705+
clonePrimitiveLiteralValue(node.operand),
10706+
);
10707+
}
10708+
Debug.fail(`Unable to clone prefixed unary expression with operator ${Debug.formatSyntaxKind(node.operator)}`);
10709+
break;
10710+
default:
10711+
Debug.assertNever(node, `Unable to clone unknown literal type.`);
10712+
}
10713+
}

tests/baselines/reference/ambientConstLiterals.js

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -61,18 +61,18 @@ declare enum E {
6161
}
6262
declare const c1 = "abc";
6363
declare const c2 = 123;
64-
declare const c3 = "abc";
65-
declare const c4 = 123;
66-
declare const c5 = 123;
67-
declare const c6 = -123;
64+
declare const c3: "abc";
65+
declare const c4: 123;
66+
declare const c5: 123;
67+
declare const c6: -123;
6868
declare const c7 = true;
69-
declare const c8 = E.A;
70-
declare const c8b = E["non identifier"];
69+
declare const c8: E.A;
70+
declare const c8b: (typeof E)["non identifier"];
7171
declare const c9: {
7272
x: string;
7373
};
7474
declare const c10: number[];
7575
declare const c11: string;
7676
declare const c12: number;
77-
declare const c13: string;
78-
declare const c14: number;
77+
declare const c13: "abc" | "def";
78+
declare const c14: 123 | 456;

tests/baselines/reference/computedEnumTypeWidening.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -170,14 +170,14 @@ declare enum E2 {
170170
D
171171
}
172172
declare function f4(): void;
173-
declare const c1 = E.B;
173+
declare const c1: E.B;
174174
declare const c2: E.B;
175175
declare let v1: E;
176176
declare let v2: E.B;
177177
declare class C {
178178
p1: E;
179179
p2: E.B;
180-
readonly p3 = E.B;
180+
readonly p3: E.B;
181181
readonly p4: E.B;
182182
}
183183
declare enum MyEnum {

tests/baselines/reference/declarationEmitConstantNoWidening.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,5 +23,5 @@ exports.Bar = Bar;
2323
//// [declarationEmitConstantNoWidening.d.ts]
2424
export declare const FOO = "FOO";
2525
export declare class Bar {
26-
readonly type = "FOO";
26+
readonly type: "FOO";
2727
}

tests/baselines/reference/declarationEmitEnumReadonlyProperty.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,6 @@ declare enum E {
3333
B = "b"
3434
}
3535
declare class C {
36-
readonly type = E.A;
36+
readonly type: E.A;
3737
}
3838
declare let x: E.A;

tests/baselines/reference/enumDeclarationEmitInitializerHasImport.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,4 +34,4 @@ export declare enum Enum {
3434
}
3535
//// [consumer.d.ts]
3636
import provider = require('./provider');
37-
export declare const value = provider.Enum.Value1;
37+
export declare const value: provider.Enum.Value1;

tests/baselines/reference/initializerWithThisPropertyAccess.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ declare class C {
110110
}
111111
declare class Foo {
112112
private bar;
113-
readonly barProp = false;
113+
readonly barProp: false;
114114
constructor();
115115
}
116116
declare class Bar {

tests/baselines/reference/nodeModulesTopLevelAwait(module=node16).js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,8 @@ for await (const y of []) { }
3737

3838

3939
//// [index.d.ts]
40-
declare const x = 1;
40+
declare const x: 1;
4141
export { x };
4242
//// [index.d.ts]
43-
declare const x = 1;
43+
declare const x: 1;
4444
export { x };

tests/baselines/reference/nodeModulesTopLevelAwait(module=nodenext).js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,8 @@ for await (const y of []) { }
3737

3838

3939
//// [index.d.ts]
40-
declare const x = 1;
40+
declare const x: 1;
4141
export { x };
4242
//// [index.d.ts]
43-
declare const x = 1;
43+
declare const x: 1;
4444
export { x };

tests/baselines/reference/strictFunctionTypes1.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,8 +81,8 @@ declare function fo(x: Object): void;
8181
declare function fs(x: string): void;
8282
declare function fx(f: (x: "def") => void): void;
8383
declare const x1: (x: string) => void;
84-
declare const x2 = "abc";
85-
declare const x3: string;
84+
declare const x2: "abc";
85+
declare const x3: "def" | "abc";
8686
declare const x4: Func<string>;
8787
declare const never: never;
8888
declare const x10: string;

tests/baselines/reference/stringLiteralTypesOverloads02.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -104,9 +104,9 @@ declare namespace Consts1 {
104104
declare const string = "string";
105105
declare const number = "number";
106106
declare const boolean = "boolean";
107-
declare const stringOrNumber: string;
108-
declare const stringOrBoolean: string;
109-
declare const booleanOrNumber: string;
110-
declare const stringOrBooleanOrNumber: string;
107+
declare const stringOrNumber: "string" | "number";
108+
declare const stringOrBoolean: "string" | "boolean";
109+
declare const booleanOrNumber: "number" | "boolean";
110+
declare const stringOrBooleanOrNumber: "string" | "number" | "boolean";
111111
declare namespace Consts2 {
112112
}

0 commit comments

Comments
 (0)