Skip to content

Commit 7fd08c4

Browse files
Simplify declaration emit for literal types.
1 parent c5db0ac commit 7fd08c4

File tree

49 files changed

+1266
-1122
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

49 files changed

+1266
-1122
lines changed

src/compiler/checker.ts

Lines changed: 4 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -48387,8 +48387,11 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
4838748387
// Get type of the symbol if this is the valid symbol otherwise get type at location
4838848388
const symbol = getSymbolOfDeclaration(declaration);
4838948389
let type = symbol && !(symbol.flags & (SymbolFlags.TypeLiteral | SymbolFlags.Signature))
48390-
? getWidenedLiteralType(getTypeOfSymbol(symbol))
48390+
? getTypeOfSymbol(symbol)
4839148391
: errorType;
48392+
if (!isReadonlySymbol(symbol)) {
48393+
type = getWidenedLiteralType(type);
48394+
}
4839248395
if (
4839348396
type.flags & TypeFlags.UniqueESSymbol &&
4839448397
type.symbol === symbol
@@ -48518,29 +48521,6 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
4851848521
return undefined;
4851948522
}
4852048523

48521-
function isLiteralConstDeclaration(node: VariableDeclaration | PropertyDeclaration | PropertySignature | ParameterDeclaration): boolean {
48522-
if (isDeclarationReadonly(node) || isVariableDeclaration(node) && isVarConstLike(node)) {
48523-
return isFreshLiteralType(getTypeOfSymbol(getSymbolOfDeclaration(node)));
48524-
}
48525-
return false;
48526-
}
48527-
48528-
function literalTypeToNode(type: FreshableType, enclosing: Node, tracker: SymbolTracker): Expression {
48529-
const enumResult = type.flags & TypeFlags.EnumLike ? nodeBuilder.symbolToExpression(type.symbol, SymbolFlags.Value, enclosing, /*flags*/ undefined, tracker)
48530-
: type === trueType ? factory.createTrue() : type === falseType && factory.createFalse();
48531-
if (enumResult) return enumResult;
48532-
const literalValue = (type as LiteralType).value;
48533-
return typeof literalValue === "object" ? factory.createBigIntLiteral(literalValue) :
48534-
typeof literalValue === "string" ? factory.createStringLiteral(literalValue) :
48535-
literalValue < 0 ? factory.createPrefixUnaryExpression(SyntaxKind.MinusToken, factory.createNumericLiteral(-literalValue)) :
48536-
factory.createNumericLiteral(literalValue);
48537-
}
48538-
48539-
function createLiteralConstValue(node: VariableDeclaration | PropertyDeclaration | PropertySignature | ParameterDeclaration, tracker: SymbolTracker) {
48540-
const type = getTypeOfSymbol(getSymbolOfDeclaration(node));
48541-
return literalTypeToNode(type as FreshableType, node, tracker);
48542-
}
48543-
4854448524
function getJsxFactoryEntity(location: Node): EntityName | undefined {
4854548525
return location ? (getJsxNamespace(location), (getSourceFileOfNode(location).localJsxFactory || _jsxFactoryEntity)) : _jsxFactoryEntity;
4854648526
}
@@ -48617,7 +48597,6 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
4861748597
createTypeOfDeclaration,
4861848598
createReturnTypeOfSignatureDeclaration,
4861948599
createTypeOfExpression,
48620-
createLiteralConstValue,
4862148600
isSymbolAccessible,
4862248601
isEntityNameVisible,
4862348602
getConstantValue: nodeIn => {
@@ -48637,7 +48616,6 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
4863748616
},
4863848617
getTypeReferenceDirectivesForEntityName,
4863948618
getTypeReferenceDirectivesForSymbol,
48640-
isLiteralConstDeclaration,
4864148619
isLateBound: (nodeIn: Declaration): nodeIn is LateBoundDeclaration => {
4864248620
const node = getParseTreeNode(nodeIn, isDeclaration);
4864348621
const symbol = node && getSymbolOfDeclaration(node);

src/compiler/emitter.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1168,7 +1168,6 @@ export const notImplementedResolver: EmitResolver = {
11681168
createTypeOfDeclaration: notImplemented,
11691169
createReturnTypeOfSignatureDeclaration: notImplemented,
11701170
createTypeOfExpression: notImplemented,
1171-
createLiteralConstValue: notImplemented,
11721171
isSymbolAccessible: notImplemented,
11731172
isEntityNameVisible: notImplemented,
11741173
// Returns the constant value this property access resolves to: notImplemented, or 'undefined' for a non-constant
@@ -1182,7 +1181,6 @@ export const notImplementedResolver: EmitResolver = {
11821181
getExternalModuleFileFromDeclaration: notImplemented,
11831182
getTypeReferenceDirectivesForEntityName: notImplemented,
11841183
getTypeReferenceDirectivesForSymbol: notImplemented,
1185-
isLiteralConstDeclaration: notImplemented,
11861184
getJsxFactoryEntity: notImplemented,
11871185
getJsxFragmentFactoryEntity: notImplemented,
11881186
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,
@@ -98,6 +99,7 @@ import {
9899
isClassElement,
99100
isComputedPropertyName,
100101
isDeclaration,
102+
isDeclarationReadonly,
101103
isEntityName,
102104
isEntityNameExpression,
103105
isExpandoPropertyDeclaration,
@@ -127,6 +129,7 @@ import {
127129
isModifier,
128130
isModuleDeclaration,
129131
isOmittedExpression,
132+
isPrimitiveLiteralValue,
130133
isPrivateIdentifier,
131134
isPropertySignature,
132135
isSemicolonClassElement,
@@ -146,6 +149,7 @@ import {
146149
isTypeQueryNode,
147150
isUnparsedSource,
148151
isVarAwaitUsing,
152+
isVarConstLike,
149153
isVariableDeclaration,
150154
isVarUsing,
151155
LateBoundDeclaration,
@@ -177,6 +181,7 @@ import {
177181
parseNodeFactory,
178182
pathContainsNodeModules,
179183
pathIsRelative,
184+
PrimitiveLiteral,
180185
PropertyDeclaration,
181186
PropertySignature,
182187
pushIfUnique,
@@ -707,13 +712,16 @@ export function transformDeclarations(context: TransformationContext) {
707712
return newParam;
708713
}
709714

710-
function shouldPrintWithInitializer(node: Node) {
711-
return canHaveLiteralInitializer(node) && resolver.isLiteralConstDeclaration(getParseTreeNode(node) as CanHaveLiteralInitializer); // TODO: Make safe
715+
function shouldPrintWithInitializer(node: Node): node is Node & { initializer: PrimitiveLiteral; } {
716+
if (canHaveLiteralInitializer(node) && node.initializer && !node.type && isPrimitiveLiteralValue(node.initializer)) {
717+
return isDeclarationReadonly(node) || isVariableDeclaration(node) && isVarConstLike(node);
718+
}
719+
return false;
712720
}
713721

714722
function ensureNoInitializer(node: CanHaveLiteralInitializer) {
715723
if (shouldPrintWithInitializer(node)) {
716-
return resolver.createLiteralConstValue(getParseTreeNode(node) as CanHaveLiteralInitializer, symbolTracker); // TODO: Make safe
724+
return clonePrimitiveLiteralValue(node.initializer);
717725
}
718726
return undefined;
719727
}
@@ -1956,7 +1964,7 @@ function getTypeAnnotationFromAccessor(accessor: AccessorDeclaration): TypeNode
19561964
}
19571965

19581966
type CanHaveLiteralInitializer = VariableDeclaration | PropertyDeclaration | PropertySignature | ParameterDeclaration;
1959-
function canHaveLiteralInitializer(node: Node): boolean {
1967+
function canHaveLiteralInitializer(node: Node): node is CanHaveLiteralInitializer {
19601968
switch (node.kind) {
19611969
case SyntaxKind.PropertyDeclaration:
19621970
case SyntaxKind.PropertySignature:

src/compiler/types.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1384,6 +1384,15 @@ export type HasIllegalModifiers =
13841384
| MissingDeclaration
13851385
| NamespaceExportDeclaration;
13861386

1387+
/** @internal */
1388+
export type PrimitiveLiteral =
1389+
| BooleanLiteral
1390+
| NumericLiteral
1391+
| StringLiteral
1392+
| NoSubstitutionTemplateLiteral
1393+
| BigIntLiteral
1394+
| PrefixUnaryExpression;
1395+
13871396
/**
13881397
* Declarations that can contain other declarations. Corresponds with `ContainerFlags.IsContainer` in binder.ts.
13891398
*
@@ -5706,7 +5715,6 @@ export interface EmitResolver {
57065715
createTypeOfDeclaration(declaration: AccessorDeclaration | VariableLikeDeclaration | PropertyAccessExpression | ElementAccessExpression | BinaryExpression, enclosingDeclaration: Node, flags: NodeBuilderFlags, tracker: SymbolTracker, addUndefined?: boolean): TypeNode | undefined;
57075716
createReturnTypeOfSignatureDeclaration(signatureDeclaration: SignatureDeclaration, enclosingDeclaration: Node, flags: NodeBuilderFlags, tracker: SymbolTracker): TypeNode | undefined;
57085717
createTypeOfExpression(expr: Expression, enclosingDeclaration: Node, flags: NodeBuilderFlags, tracker: SymbolTracker): TypeNode | undefined;
5709-
createLiteralConstValue(node: VariableDeclaration | PropertyDeclaration | PropertySignature | ParameterDeclaration, tracker: SymbolTracker): Expression;
57105718
isSymbolAccessible(symbol: Symbol, enclosingDeclaration: Node | undefined, meaning: SymbolFlags | undefined, shouldComputeAliasToMarkVisible: boolean): SymbolAccessibilityResult;
57115719
isEntityNameVisible(entityName: EntityNameOrEntityNameExpression, enclosingDeclaration: Node): SymbolVisibilityResult;
57125720
// Returns the constant value this property access resolves to, or 'undefined' for a non-constant
@@ -5720,7 +5728,6 @@ export interface EmitResolver {
57205728
getExternalModuleFileFromDeclaration(declaration: ImportEqualsDeclaration | ImportDeclaration | ExportDeclaration | ModuleDeclaration | ImportTypeNode | ImportCall): SourceFile | undefined;
57215729
getTypeReferenceDirectivesForEntityName(name: EntityNameOrEntityNameExpression): [specifier: string, mode: ResolutionMode][] | undefined;
57225730
getTypeReferenceDirectivesForSymbol(symbol: Symbol, meaning?: SymbolFlags): [specifier: string, mode: ResolutionMode][] | undefined;
5723-
isLiteralConstDeclaration(node: VariableDeclaration | PropertyDeclaration | PropertySignature | ParameterDeclaration): boolean;
57245731
getJsxFactoryEntity(location?: Node): EntityName | undefined;
57255732
getJsxFragmentFactoryEntity(location?: Node): EntityName | undefined;
57265733
getAllAccessorDeclarations(declaration: AccessorDeclaration): AllAccessorDeclarations;

src/compiler/utilities.ts

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -447,6 +447,7 @@ import {
447447
Pattern,
448448
PostfixUnaryExpression,
449449
PrefixUnaryExpression,
450+
PrimitiveLiteral,
450451
PrinterOptions,
451452
PrintHandlers,
452453
PrivateIdentifier,
@@ -2350,6 +2351,14 @@ export function isLet(node: Node): boolean {
23502351
return (getCombinedNodeFlags(node) & NodeFlags.BlockScoped) === NodeFlags.Let;
23512352
}
23522353

2354+
/** @internal */
2355+
export function isVarConstLike(node: VariableDeclaration | VariableDeclarationList) {
2356+
const blockScopeKind = getCombinedNodeFlags(node) & NodeFlags.BlockScoped;
2357+
return blockScopeKind === NodeFlags.Const ||
2358+
blockScopeKind === NodeFlags.Using ||
2359+
blockScopeKind === NodeFlags.AwaitUsing;
2360+
}
2361+
23532362
/** @internal */
23542363
export function isSuperCall(n: Node): n is SuperCall {
23552364
return n.kind === SyntaxKind.CallExpression && (n as CallExpression).expression.kind === SyntaxKind.SuperKeyword;
@@ -10671,3 +10680,66 @@ export function replaceFirstStar(s: string, replacement: string): string {
1067110680
export function getNameFromImportAttribute(node: ImportAttribute) {
1067210681
return isIdentifier(node.name) ? node.name.escapedText : escapeLeadingUnderscores(node.name.text);
1067310682
}
10683+
10684+
/** @internal */
10685+
export function isPrimitiveLiteralValue(node: Expression, includeBigInt = true): node is PrimitiveLiteral {
10686+
Debug.type<PrimitiveLiteral>(node);
10687+
switch (node.kind) {
10688+
case SyntaxKind.TrueKeyword:
10689+
case SyntaxKind.FalseKeyword:
10690+
case SyntaxKind.NumericLiteral:
10691+
case SyntaxKind.StringLiteral:
10692+
case SyntaxKind.NoSubstitutionTemplateLiteral:
10693+
return true;
10694+
case SyntaxKind.BigIntLiteral:
10695+
return includeBigInt;
10696+
case SyntaxKind.PrefixUnaryExpression:
10697+
if (node.operator === SyntaxKind.MinusToken) {
10698+
return isNumericLiteral(node.operand) || (includeBigInt && isBigIntLiteral(node.operand));
10699+
}
10700+
if (node.operator === SyntaxKind.PlusToken) {
10701+
return isNumericLiteral(node.operand);
10702+
}
10703+
return false;
10704+
default:
10705+
assertType<never>(node);
10706+
return false;
10707+
}
10708+
}
10709+
10710+
/**
10711+
* @internal
10712+
*
10713+
* Clone literal value while normalizing it (converts octals/hex to base 10, uses double quotes strings)
10714+
*/
10715+
export function clonePrimitiveLiteralValue<T extends PrimitiveLiteral>(node: T): T;
10716+
export function clonePrimitiveLiteralValue(node: PrimitiveLiteral): PrimitiveLiteral {
10717+
switch (node.kind) {
10718+
case SyntaxKind.NumericLiteral:
10719+
return factory.createNumericLiteral(node.text);
10720+
case SyntaxKind.BigIntLiteral:
10721+
return factory.createBigIntLiteral({ negative: false, base10Value: parsePseudoBigInt(node.text) });
10722+
case SyntaxKind.StringLiteral:
10723+
case SyntaxKind.NoSubstitutionTemplateLiteral:
10724+
return factory.createStringLiteral(node.text);
10725+
case SyntaxKind.FalseKeyword:
10726+
return factory.createFalse();
10727+
case SyntaxKind.TrueKeyword:
10728+
return factory.createTrue();
10729+
case SyntaxKind.PrefixUnaryExpression:
10730+
Debug.assert(isNumericLiteral(node.operand) || isBigIntLiteral(node.operand));
10731+
if (node.operator === SyntaxKind.PlusToken) {
10732+
return clonePrimitiveLiteralValue(node.operand);
10733+
}
10734+
else if (node.operator === SyntaxKind.MinusToken) {
10735+
return factory.createPrefixUnaryExpression(
10736+
node.operator,
10737+
clonePrimitiveLiteralValue(node.operand),
10738+
);
10739+
}
10740+
Debug.fail(`Unable to clone prefixed unary expression with operator ${Debug.formatSyntaxKind(node.operator)}`);
10741+
break;
10742+
default:
10743+
Debug.assertNever(node, `Unable to clone unknown literal type.`);
10744+
}
10745+
}

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
@@ -79,8 +79,8 @@ finally {
7979

8080

8181
//// [index.d.ts]
82-
declare const x = 1;
82+
declare const x: 1;
8383
export { x };
8484
//// [index.d.ts]
85-
declare const x = 1;
85+
declare const x: 1;
8686
export { x };

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,8 +79,8 @@ finally {
7979

8080

8181
//// [index.d.ts]
82-
declare const x = 1;
82+
declare const x: 1;
8383
export { x };
8484
//// [index.d.ts]
85-
declare const x = 1;
85+
declare const x: 1;
8686
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)