Skip to content

Commit 739f5f2

Browse files
committed
Adds additional logic to determine how to emit a type reference for decorator metadata
1 parent 9eab885 commit 739f5f2

3 files changed

Lines changed: 123 additions & 15 deletions

File tree

src/compiler/checker.ts

Lines changed: 54 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3306,6 +3306,15 @@ namespace ts {
33063306
return getSignaturesOfObjectOrUnionType(getApparentType(type), kind);
33073307
}
33083308

3309+
function typeHasConstructSignatures(type: Type): boolean {
3310+
let apparentType = getApparentType(type);
3311+
if (apparentType.flags & (TypeFlags.ObjectType | TypeFlags.Union)) {
3312+
let resolved = resolveObjectOrUnionTypeMembers(<ObjectType>type);
3313+
return resolved.constructSignatures.length > 0;
3314+
}
3315+
return false;
3316+
}
3317+
33093318
function typeHasCallOrConstructSignatures(type: Type): boolean {
33103319
let apparentType = getApparentType(type);
33113320
if (apparentType.flags & (TypeFlags.ObjectType | TypeFlags.Union)) {
@@ -13207,15 +13216,53 @@ namespace ts {
1320713216

1320813217
return undefined;
1320913218
}
13219+
13220+
function isFunctionType(type: Type): boolean {
13221+
return type.flags & TypeFlags.ObjectType && getSignaturesOfType(type, SignatureKind.Call).length > 0;
13222+
}
1321013223

13211-
function isTypeReferenceWithValueDeclaration(node: TypeReferenceNode): boolean {
13224+
function isTypeWithValue(node: TypeReferenceNode): TypeWithValueResolutionResult {
13225+
// Resolve the symbol as a value to ensure the type can be reached at runtime during emit.
1321213226
let symbol = resolveEntityName(node.typeName, SymbolFlags.Value, /*ignoreErrors*/ true);
13213-
if (symbol.valueDeclaration) {
13214-
let type = getTypeOfSymbol(symbol);
13215-
return typeHasCallOrConstructSignatures(type);
13227+
let constructorType = symbol ? getTypeOfSymbol(symbol) : undefined;
13228+
if (constructorType && isConstructorType(constructorType)) {
13229+
return TypeWithValueResolutionResult.ConstructorTypeWithValue;
13230+
}
13231+
13232+
let type = getTypeFromTypeNode(node);
13233+
if (type === unknownType) {
13234+
return TypeWithValueResolutionResult.Unknown;
13235+
}
13236+
else if (type.flags & TypeFlags.Any) {
13237+
return TypeWithValueResolutionResult.ObjectType;
13238+
}
13239+
else if (allConstituentTypesHaveKind(type, TypeFlags.Void)) {
13240+
return TypeWithValueResolutionResult.VoidType;
13241+
}
13242+
else if (allConstituentTypesHaveKind(type, TypeFlags.Boolean)) {
13243+
return TypeWithValueResolutionResult.BooleanType;
13244+
}
13245+
else if (allConstituentTypesHaveKind(type, TypeFlags.NumberLike)) {
13246+
return TypeWithValueResolutionResult.NumberType;
13247+
}
13248+
else if (allConstituentTypesHaveKind(type, TypeFlags.StringLike)) {
13249+
return TypeWithValueResolutionResult.StringType;
13250+
}
13251+
else if (allConstituentTypesHaveKind(type, TypeFlags.Tuple)) {
13252+
return TypeWithValueResolutionResult.ArrayType;
13253+
}
13254+
else if (allConstituentTypesHaveKind(type, TypeFlags.ESSymbol)) {
13255+
return TypeWithValueResolutionResult.ESSymbolType;
13256+
}
13257+
else if (isFunctionType(type)) {
13258+
return TypeWithValueResolutionResult.FunctionType;
13259+
}
13260+
else if (isArrayType(type)) {
13261+
return TypeWithValueResolutionResult.ArrayType;
13262+
}
13263+
else {
13264+
return TypeWithValueResolutionResult.ObjectType;
1321613265
}
13217-
13218-
return false;
1321913266
}
1322013267

1322113268
function writeTypeOfDeclaration(declaration: AccessorDeclaration | VariableLikeDeclaration, enclosingDeclaration: Node, flags: TypeFormatFlags, writer: SymbolWriter) {
@@ -13315,7 +13362,7 @@ namespace ts {
1331513362
collectLinkedAliases,
1331613363
getBlockScopedVariableId,
1331713364
getReferencedValueDeclaration,
13318-
isTypeReferenceWithValueDeclaration,
13365+
isTypeWithValue,
1331913366
};
1332013367
}
1332113368

src/compiler/emitter.ts

Lines changed: 54 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4710,13 +4710,59 @@ var __param = (this && this.__param) || function (paramIndex, decorator) {
47104710
/** Serializes a TypeReferenceNode to an appropriate JS constructor value. Used by the __metadata decorator. */
47114711
function emitSerializedTypeReferenceNode(node: TypeReferenceNode) {
47124712
let typeName = node.typeName;
4713-
if (resolver.isTypeReferenceWithValueDeclaration(node)) {
4714-
emitEntityNameAsExpression(typeName, /*useFallback*/ false);
4715-
}
4716-
else {
4717-
write("(");
4718-
emitEntityNameAsExpression(typeName, /*useFallback*/ true);
4719-
write(") || Object");
4713+
let result = resolver.isTypeWithValue(node);
4714+
switch (result) {
4715+
case TypeWithValueResolutionResult.Unknown:
4716+
let temp = createAndRecordTempVariable(TempFlags.Auto);
4717+
write("(typeof (");
4718+
emitNodeWithoutSourceMap(temp);
4719+
write(" = ")
4720+
emitEntityNameAsExpression(typeName, /*useFallback*/ true);
4721+
write(") === 'function' && ");
4722+
emitNodeWithoutSourceMap(temp);
4723+
write(") || Object");
4724+
break;
4725+
4726+
case TypeWithValueResolutionResult.ConstructorTypeWithValue:
4727+
emitEntityNameAsExpression(typeName, /*useFallback*/ false);
4728+
break;
4729+
4730+
case TypeWithValueResolutionResult.VoidType:
4731+
write("void 0");
4732+
break;
4733+
4734+
case TypeWithValueResolutionResult.BooleanType:
4735+
write("Boolean");
4736+
break;
4737+
4738+
case TypeWithValueResolutionResult.NumberType:
4739+
write("Number");
4740+
break;
4741+
4742+
case TypeWithValueResolutionResult.StringType:
4743+
write("String");
4744+
break;
4745+
4746+
case TypeWithValueResolutionResult.ArrayType:
4747+
write("Array");
4748+
break;
4749+
4750+
case TypeWithValueResolutionResult.ESSymbolType:
4751+
if (languageVersion < ScriptTarget.ES6) {
4752+
write("typeof Symbol === 'function' ? Symbol : Object");
4753+
}
4754+
else {
4755+
write("Symbol");
4756+
}
4757+
break;
4758+
4759+
case TypeWithValueResolutionResult.FunctionType:
4760+
write("Function");
4761+
break;
4762+
4763+
case TypeWithValueResolutionResult.ObjectType:
4764+
write("Object");
4765+
break;
47204766
}
47214767
}
47224768

@@ -5927,6 +5973,7 @@ var __param = (this && this.__param) || function (paramIndex, decorator) {
59275973
Debug.assert(!exportFunctionForFile);
59285974
// make sure that name of 'exports' function does not conflict with existing identifiers
59295975
exportFunctionForFile = makeUniqueName("exports");
5976+
writeLine();
59305977
write("System.register(");
59315978
if (node.moduleName) {
59325979
write(`"${node.moduleName}", `);

src/compiler/types.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1501,6 +1501,20 @@ namespace ts {
15011501
export interface SymbolAccessiblityResult extends SymbolVisibilityResult {
15021502
errorModuleName?: string // If the symbol is not visible from module, module's name
15031503
}
1504+
1505+
/* @internal */
1506+
export enum TypeWithValueResolutionResult {
1507+
Unknown,
1508+
ConstructorTypeWithValue,
1509+
VoidType,
1510+
NumberType,
1511+
StringType,
1512+
BooleanType,
1513+
ArrayType,
1514+
ESSymbolType,
1515+
FunctionType,
1516+
ObjectType,
1517+
}
15041518

15051519
/* @internal */
15061520
export interface EmitResolver {
@@ -1525,7 +1539,7 @@ namespace ts {
15251539
getConstantValue(node: EnumMember | PropertyAccessExpression | ElementAccessExpression): number;
15261540
getBlockScopedVariableId(node: Identifier): number;
15271541
getReferencedValueDeclaration(reference: Identifier): Declaration;
1528-
isTypeReferenceWithValueDeclaration(node: TypeReferenceNode): boolean;
1542+
isTypeWithValue(node: TypeReferenceNode): TypeWithValueResolutionResult;
15291543
}
15301544

15311545
export const enum SymbolFlags {

0 commit comments

Comments
 (0)