Adds additional logic to determine how to emit a type reference for decorator metadata

This commit is contained in:
Ron Buckton
2015-06-30 20:19:25 -07:00
parent 9eab88570f
commit 739f5f25cf
3 changed files with 123 additions and 15 deletions
+54 -7
View File
@@ -3306,6 +3306,15 @@ namespace ts {
return getSignaturesOfObjectOrUnionType(getApparentType(type), kind);
}
function typeHasConstructSignatures(type: Type): boolean {
let apparentType = getApparentType(type);
if (apparentType.flags & (TypeFlags.ObjectType | TypeFlags.Union)) {
let resolved = resolveObjectOrUnionTypeMembers(<ObjectType>type);
return resolved.constructSignatures.length > 0;
}
return false;
}
function typeHasCallOrConstructSignatures(type: Type): boolean {
let apparentType = getApparentType(type);
if (apparentType.flags & (TypeFlags.ObjectType | TypeFlags.Union)) {
@@ -13207,15 +13216,53 @@ namespace ts {
return undefined;
}
function isFunctionType(type: Type): boolean {
return type.flags & TypeFlags.ObjectType && getSignaturesOfType(type, SignatureKind.Call).length > 0;
}
function isTypeReferenceWithValueDeclaration(node: TypeReferenceNode): boolean {
function isTypeWithValue(node: TypeReferenceNode): TypeWithValueResolutionResult {
// Resolve the symbol as a value to ensure the type can be reached at runtime during emit.
let symbol = resolveEntityName(node.typeName, SymbolFlags.Value, /*ignoreErrors*/ true);
if (symbol.valueDeclaration) {
let type = getTypeOfSymbol(symbol);
return typeHasCallOrConstructSignatures(type);
let constructorType = symbol ? getTypeOfSymbol(symbol) : undefined;
if (constructorType && isConstructorType(constructorType)) {
return TypeWithValueResolutionResult.ConstructorTypeWithValue;
}
let type = getTypeFromTypeNode(node);
if (type === unknownType) {
return TypeWithValueResolutionResult.Unknown;
}
else if (type.flags & TypeFlags.Any) {
return TypeWithValueResolutionResult.ObjectType;
}
else if (allConstituentTypesHaveKind(type, TypeFlags.Void)) {
return TypeWithValueResolutionResult.VoidType;
}
else if (allConstituentTypesHaveKind(type, TypeFlags.Boolean)) {
return TypeWithValueResolutionResult.BooleanType;
}
else if (allConstituentTypesHaveKind(type, TypeFlags.NumberLike)) {
return TypeWithValueResolutionResult.NumberType;
}
else if (allConstituentTypesHaveKind(type, TypeFlags.StringLike)) {
return TypeWithValueResolutionResult.StringType;
}
else if (allConstituentTypesHaveKind(type, TypeFlags.Tuple)) {
return TypeWithValueResolutionResult.ArrayType;
}
else if (allConstituentTypesHaveKind(type, TypeFlags.ESSymbol)) {
return TypeWithValueResolutionResult.ESSymbolType;
}
else if (isFunctionType(type)) {
return TypeWithValueResolutionResult.FunctionType;
}
else if (isArrayType(type)) {
return TypeWithValueResolutionResult.ArrayType;
}
else {
return TypeWithValueResolutionResult.ObjectType;
}
return false;
}
function writeTypeOfDeclaration(declaration: AccessorDeclaration | VariableLikeDeclaration, enclosingDeclaration: Node, flags: TypeFormatFlags, writer: SymbolWriter) {
@@ -13315,7 +13362,7 @@ namespace ts {
collectLinkedAliases,
getBlockScopedVariableId,
getReferencedValueDeclaration,
isTypeReferenceWithValueDeclaration,
isTypeWithValue,
};
}
+54 -7
View File
@@ -4710,13 +4710,59 @@ var __param = (this && this.__param) || function (paramIndex, decorator) {
/** Serializes a TypeReferenceNode to an appropriate JS constructor value. Used by the __metadata decorator. */
function emitSerializedTypeReferenceNode(node: TypeReferenceNode) {
let typeName = node.typeName;
if (resolver.isTypeReferenceWithValueDeclaration(node)) {
emitEntityNameAsExpression(typeName, /*useFallback*/ false);
}
else {
write("(");
emitEntityNameAsExpression(typeName, /*useFallback*/ true);
write(") || Object");
let result = resolver.isTypeWithValue(node);
switch (result) {
case TypeWithValueResolutionResult.Unknown:
let temp = createAndRecordTempVariable(TempFlags.Auto);
write("(typeof (");
emitNodeWithoutSourceMap(temp);
write(" = ")
emitEntityNameAsExpression(typeName, /*useFallback*/ true);
write(") === 'function' && ");
emitNodeWithoutSourceMap(temp);
write(") || Object");
break;
case TypeWithValueResolutionResult.ConstructorTypeWithValue:
emitEntityNameAsExpression(typeName, /*useFallback*/ false);
break;
case TypeWithValueResolutionResult.VoidType:
write("void 0");
break;
case TypeWithValueResolutionResult.BooleanType:
write("Boolean");
break;
case TypeWithValueResolutionResult.NumberType:
write("Number");
break;
case TypeWithValueResolutionResult.StringType:
write("String");
break;
case TypeWithValueResolutionResult.ArrayType:
write("Array");
break;
case TypeWithValueResolutionResult.ESSymbolType:
if (languageVersion < ScriptTarget.ES6) {
write("typeof Symbol === 'function' ? Symbol : Object");
}
else {
write("Symbol");
}
break;
case TypeWithValueResolutionResult.FunctionType:
write("Function");
break;
case TypeWithValueResolutionResult.ObjectType:
write("Object");
break;
}
}
@@ -5927,6 +5973,7 @@ var __param = (this && this.__param) || function (paramIndex, decorator) {
Debug.assert(!exportFunctionForFile);
// make sure that name of 'exports' function does not conflict with existing identifiers
exportFunctionForFile = makeUniqueName("exports");
writeLine();
write("System.register(");
if (node.moduleName) {
write(`"${node.moduleName}", `);
+15 -1
View File
@@ -1501,6 +1501,20 @@ namespace ts {
export interface SymbolAccessiblityResult extends SymbolVisibilityResult {
errorModuleName?: string // If the symbol is not visible from module, module's name
}
/* @internal */
export enum TypeWithValueResolutionResult {
Unknown,
ConstructorTypeWithValue,
VoidType,
NumberType,
StringType,
BooleanType,
ArrayType,
ESSymbolType,
FunctionType,
ObjectType,
}
/* @internal */
export interface EmitResolver {
@@ -1525,7 +1539,7 @@ namespace ts {
getConstantValue(node: EnumMember | PropertyAccessExpression | ElementAccessExpression): number;
getBlockScopedVariableId(node: Identifier): number;
getReferencedValueDeclaration(reference: Identifier): Declaration;
isTypeReferenceWithValueDeclaration(node: TypeReferenceNode): boolean;
isTypeWithValue(node: TypeReferenceNode): TypeWithValueResolutionResult;
}
export const enum SymbolFlags {