diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index c97d17f9193..47f9acca7d1 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -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(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, }; } diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index 3b5602e4f3c..6a3afc06168 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -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}", `); diff --git a/src/compiler/types.ts b/src/compiler/types.ts index d563c9c07ba..4fe2cf59830 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -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 {