• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1import {
2    AccessorDeclaration, ArrayLiteralExpression, BigIntLiteral, BinaryExpression, Block, CaseBlock,
3    ClassLikeDeclaration, ConditionalExpression, ConditionalTypeNode, Debug, EntityName, Expression, factory,
4    findAncestor, FunctionLikeDeclaration, getAllAccessorDeclarations, getEffectiveReturnTypeNode, getEmitScriptTarget,
5    getFirstConstructorWithBody, getParseTreeNode, getRestParameterElementType, getSetAccessorTypeAnnotationNode,
6    getStrictOptionValue, Identifier, isAsyncFunction, isBinaryExpression, isClassLike, isConditionalExpression,
7    isConditionalTypeNode, isFunctionLike, isGeneratedIdentifier, isIdentifier, isLiteralTypeNode, isNumericLiteral,
8    isParenthesizedExpression, isPropertyAccessExpression, isStringLiteral, isTypeOfExpression, isVoidExpression,
9    JSDocNonNullableType, JSDocNullableType, JSDocOptionalType, LiteralTypeNode, MethodDeclaration, ModuleBlock, Node,
10    nodeIsPresent, NumericLiteral, ParameterDeclaration, parseNodeFactory, PrefixUnaryExpression,
11    PropertyAccessEntityNameExpression, PropertyDeclaration, QualifiedName, ScriptTarget, setParent, setTextRange,
12    SignatureDeclaration, skipTypeParentheses, SourceFile, SyntaxKind, TransformationContext, TypeNode,
13    TypeOperatorNode, TypePredicateNode, TypeReferenceNode, TypeReferenceSerializationKind, UnionOrIntersectionTypeNode,
14    VoidExpression,
15} from "../_namespaces/ts";
16
17/** @internal */
18export type SerializedEntityName =
19    | Identifier // Globals (i.e., `String`, `Number`, etc.)
20 // Globals (i.e., `String`, `Number`, etc.)
21    | PropertyAccessEntityNameExpression // `A.B`
22 // `A.B`
23    ;
24
25
26/** @internal */
27export type SerializedTypeNode =
28    | SerializedEntityName
29    | ConditionalExpression // Type Reference or Global fallback
30 // Type Reference or Global fallback
31    | VoidExpression // `void 0` used for null/undefined/never
32 // `void 0` used for null/undefined/never
33    ;
34
35/** @internal */
36export interface RuntimeTypeSerializerContext {
37    /** Specifies the current lexical block scope */
38    currentLexicalScope: SourceFile | Block | ModuleBlock | CaseBlock;
39    /** Specifies the containing `class`, but only when there is no other block scope between the current location and the `class`. */
40    currentNameScope: ClassLikeDeclaration | undefined;
41}
42
43/** @internal */
44export interface RuntimeTypeSerializer {
45    /**
46     * Serializes a type node for use with decorator type metadata.
47     *
48     * Types are serialized in the following fashion:
49     * - Void types point to "undefined" (e.g. "void 0")
50     * - Function and Constructor types point to the global "Function" constructor.
51     * - Interface types with a call or construct signature types point to the global
52     *   "Function" constructor.
53     * - Array and Tuple types point to the global "Array" constructor.
54     * - Type predicates and booleans point to the global "Boolean" constructor.
55     * - String literal types and strings point to the global "String" constructor.
56     * - Enum and number types point to the global "Number" constructor.
57     * - Symbol types point to the global "Symbol" constructor.
58     * - Type references to classes (or class-like variables) point to the constructor for the class.
59     * - Anything else points to the global "Object" constructor.
60     *
61     * @param node The type node to serialize.
62     */
63    serializeTypeNode(serializerContext: RuntimeTypeSerializerContext, node: TypeNode): Expression;
64    /**
65     * Serializes the type of a node for use with decorator type metadata.
66     * @param node The node that should have its type serialized.
67     */
68    serializeTypeOfNode(serializerContext: RuntimeTypeSerializerContext, node: PropertyDeclaration | ParameterDeclaration | AccessorDeclaration | ClassLikeDeclaration | MethodDeclaration): Expression;
69    /**
70     * Serializes the types of the parameters of a node for use with decorator type metadata.
71     * @param node The node that should have its parameter types serialized.
72     */
73    serializeParameterTypesOfNode(serializerContext: RuntimeTypeSerializerContext, node: Node, container: ClassLikeDeclaration): ArrayLiteralExpression;
74    /**
75     * Serializes the return type of a node for use with decorator type metadata.
76     * @param node The node that should have its return type serialized.
77     */
78    serializeReturnTypeOfNode(serializerContext: RuntimeTypeSerializerContext, node: Node): SerializedTypeNode;
79}
80
81/** @internal */
82export function createRuntimeTypeSerializer(context: TransformationContext): RuntimeTypeSerializer {
83    const {
84        hoistVariableDeclaration
85    } = context;
86
87    const resolver = context.getEmitResolver();
88    const compilerOptions = context.getCompilerOptions();
89    const languageVersion = getEmitScriptTarget(compilerOptions);
90    const strictNullChecks = getStrictOptionValue(compilerOptions, "strictNullChecks");
91
92    let currentLexicalScope: SourceFile | CaseBlock | ModuleBlock | Block;
93    let currentNameScope: ClassLikeDeclaration | undefined;
94
95    return {
96        serializeTypeNode: (serializerContext, node) => setSerializerContextAnd(serializerContext, serializeTypeNode, node),
97        serializeTypeOfNode: (serializerContext, node) => setSerializerContextAnd(serializerContext, serializeTypeOfNode, node),
98        serializeParameterTypesOfNode: (serializerContext, node, container) => setSerializerContextAnd(serializerContext, serializeParameterTypesOfNode, node, container),
99        serializeReturnTypeOfNode: (serializerContext, node) => setSerializerContextAnd(serializerContext, serializeReturnTypeOfNode, node),
100    };
101
102    function setSerializerContextAnd<TNode extends Node | undefined, R>(serializerContext: RuntimeTypeSerializerContext, cb: (node: TNode) => R, node: TNode): R;
103    function setSerializerContextAnd<TNode extends Node | undefined, T, R>(serializerContext: RuntimeTypeSerializerContext, cb: (node: TNode, arg: T) => R, node: TNode, arg: T): R;
104    function setSerializerContextAnd<TNode extends Node | undefined, T, R>(serializerContext: RuntimeTypeSerializerContext, cb: (node: TNode, arg?: T) => R, node: TNode, arg?: T) {
105        const savedCurrentLexicalScope = currentLexicalScope;
106        const savedCurrentNameScope = currentNameScope;
107
108        currentLexicalScope = serializerContext.currentLexicalScope;
109        currentNameScope = serializerContext.currentNameScope;
110
111        const result = arg === undefined ? cb(node) : cb(node, arg);
112
113        currentLexicalScope = savedCurrentLexicalScope;
114        currentNameScope = savedCurrentNameScope;
115        return result;
116    }
117
118    function getAccessorTypeNode(node: AccessorDeclaration) {
119        const accessors = resolver.getAllAccessorDeclarations(node);
120        return accessors.setAccessor && getSetAccessorTypeAnnotationNode(accessors.setAccessor)
121            || accessors.getAccessor && getEffectiveReturnTypeNode(accessors.getAccessor);
122    }
123
124    /**
125     * Serializes the type of a node for use with decorator type metadata.
126     * @param node The node that should have its type serialized.
127     */
128    function serializeTypeOfNode(node: PropertyDeclaration | ParameterDeclaration | AccessorDeclaration | ClassLikeDeclaration | MethodDeclaration): SerializedTypeNode {
129        switch (node.kind) {
130            case SyntaxKind.PropertyDeclaration:
131            case SyntaxKind.Parameter:
132                return serializeTypeNode(node.type);
133            case SyntaxKind.SetAccessor:
134            case SyntaxKind.GetAccessor:
135                return serializeTypeNode(getAccessorTypeNode(node));
136            case SyntaxKind.ClassDeclaration:
137            case SyntaxKind.ClassExpression:
138            case SyntaxKind.MethodDeclaration:
139                return factory.createIdentifier("Function");
140            default:
141                return factory.createVoidZero();
142        }
143    }
144
145    /**
146     * Serializes the type of a node for use with decorator type metadata.
147     * @param node The node that should have its type serialized.
148     */
149    function serializeParameterTypesOfNode(node: Node, container: ClassLikeDeclaration): ArrayLiteralExpression {
150        const valueDeclaration =
151            isClassLike(node)
152                ? getFirstConstructorWithBody(node)
153                : isFunctionLike(node) && nodeIsPresent((node as FunctionLikeDeclaration).body)
154                    ? node
155                    : undefined;
156
157        const expressions: SerializedTypeNode[] = [];
158        if (valueDeclaration) {
159            const parameters = getParametersOfDecoratedDeclaration(valueDeclaration, container);
160            const numParameters = parameters.length;
161            for (let i = 0; i < numParameters; i++) {
162                const parameter = parameters[i];
163                if (i === 0 && isIdentifier(parameter.name) && parameter.name.escapedText === "this") {
164                    continue;
165                }
166                if (parameter.dotDotDotToken) {
167                    expressions.push(serializeTypeNode(getRestParameterElementType(parameter.type)));
168                }
169                else {
170                    expressions.push(serializeTypeOfNode(parameter));
171                }
172            }
173        }
174
175        return factory.createArrayLiteralExpression(expressions);
176    }
177
178    function getParametersOfDecoratedDeclaration(node: SignatureDeclaration, container: ClassLikeDeclaration) {
179        if (container && node.kind === SyntaxKind.GetAccessor) {
180            const { setAccessor } = getAllAccessorDeclarations(container.members, node as AccessorDeclaration);
181            if (setAccessor) {
182                return setAccessor.parameters;
183            }
184        }
185        return node.parameters;
186    }
187
188    /**
189     * Serializes the return type of a node for use with decorator type metadata.
190     * @param node The node that should have its return type serialized.
191     */
192    function serializeReturnTypeOfNode(node: Node): SerializedTypeNode {
193        if (isFunctionLike(node) && node.type) {
194            return serializeTypeNode(node.type);
195        }
196        else if (isAsyncFunction(node)) {
197            return factory.createIdentifier("Promise");
198        }
199
200        return factory.createVoidZero();
201    }
202
203    /**
204     * Serializes a type node for use with decorator type metadata.
205     *
206     * Types are serialized in the following fashion:
207     * - Void types point to "undefined" (e.g. "void 0")
208     * - Function and Constructor types point to the global "Function" constructor.
209     * - Interface types with a call or construct signature types point to the global
210     *   "Function" constructor.
211     * - Array and Tuple types point to the global "Array" constructor.
212     * - Type predicates and booleans point to the global "Boolean" constructor.
213     * - String literal types and strings point to the global "String" constructor.
214     * - Enum and number types point to the global "Number" constructor.
215     * - Symbol types point to the global "Symbol" constructor.
216     * - Type references to classes (or class-like variables) point to the constructor for the class.
217     * - Anything else points to the global "Object" constructor.
218     *
219     * @param node The type node to serialize.
220     */
221    function serializeTypeNode(node: TypeNode | undefined): SerializedTypeNode {
222        if (node === undefined) {
223            return factory.createIdentifier("Object");
224        }
225
226        node = skipTypeParentheses(node);
227
228        switch (node.kind) {
229            case SyntaxKind.VoidKeyword:
230            case SyntaxKind.UndefinedKeyword:
231            case SyntaxKind.NeverKeyword:
232                return factory.createVoidZero();
233
234            case SyntaxKind.FunctionType:
235            case SyntaxKind.ConstructorType:
236                return factory.createIdentifier("Function");
237
238            case SyntaxKind.ArrayType:
239            case SyntaxKind.TupleType:
240                return factory.createIdentifier("Array");
241
242            case SyntaxKind.TypePredicate:
243                return (node as TypePredicateNode).assertsModifier ?
244                    factory.createVoidZero() :
245                    factory.createIdentifier("Boolean");
246
247            case SyntaxKind.BooleanKeyword:
248                return factory.createIdentifier("Boolean");
249
250            case SyntaxKind.TemplateLiteralType:
251            case SyntaxKind.StringKeyword:
252                return factory.createIdentifier("String");
253
254            case SyntaxKind.ObjectKeyword:
255                return factory.createIdentifier("Object");
256
257            case SyntaxKind.LiteralType:
258                return serializeLiteralOfLiteralTypeNode((node as LiteralTypeNode).literal);
259
260            case SyntaxKind.NumberKeyword:
261                return factory.createIdentifier("Number");
262
263            case SyntaxKind.BigIntKeyword:
264                return getGlobalConstructor("BigInt", ScriptTarget.ES2020);
265
266            case SyntaxKind.SymbolKeyword:
267                return getGlobalConstructor("Symbol", ScriptTarget.ES2015);
268
269            case SyntaxKind.TypeReference:
270                return serializeTypeReferenceNode(node as TypeReferenceNode);
271
272            case SyntaxKind.IntersectionType:
273                return serializeUnionOrIntersectionConstituents((node as UnionOrIntersectionTypeNode).types, /*isIntersection*/ true);
274
275            case SyntaxKind.UnionType:
276                return serializeUnionOrIntersectionConstituents((node as UnionOrIntersectionTypeNode).types, /*isIntersection*/ false);
277
278            case SyntaxKind.ConditionalType:
279                return serializeUnionOrIntersectionConstituents([(node as ConditionalTypeNode).trueType, (node as ConditionalTypeNode).falseType], /*isIntersection*/ false);
280
281            case SyntaxKind.TypeOperator:
282                if ((node as TypeOperatorNode).operator === SyntaxKind.ReadonlyKeyword) {
283                    return serializeTypeNode((node as TypeOperatorNode).type);
284                }
285                break;
286
287            case SyntaxKind.TypeQuery:
288            case SyntaxKind.IndexedAccessType:
289            case SyntaxKind.MappedType:
290            case SyntaxKind.TypeLiteral:
291            case SyntaxKind.AnyKeyword:
292            case SyntaxKind.UnknownKeyword:
293            case SyntaxKind.ThisType:
294            case SyntaxKind.ImportType:
295                break;
296
297            // handle JSDoc types from an invalid parse
298            case SyntaxKind.JSDocAllType:
299            case SyntaxKind.JSDocUnknownType:
300            case SyntaxKind.JSDocFunctionType:
301            case SyntaxKind.JSDocVariadicType:
302            case SyntaxKind.JSDocNamepathType:
303                break;
304
305            case SyntaxKind.JSDocNullableType:
306            case SyntaxKind.JSDocNonNullableType:
307            case SyntaxKind.JSDocOptionalType:
308                return serializeTypeNode((node as JSDocNullableType | JSDocNonNullableType | JSDocOptionalType).type);
309
310            default:
311                return Debug.failBadSyntaxKind(node);
312        }
313
314        return factory.createIdentifier("Object");
315    }
316
317    function serializeLiteralOfLiteralTypeNode(node: LiteralTypeNode["literal"]): SerializedTypeNode {
318        switch (node.kind) {
319            case SyntaxKind.StringLiteral:
320            case SyntaxKind.NoSubstitutionTemplateLiteral:
321                return factory.createIdentifier("String");
322
323            case SyntaxKind.PrefixUnaryExpression: {
324                const operand = (node as PrefixUnaryExpression).operand;
325                switch (operand.kind) {
326                    case SyntaxKind.NumericLiteral:
327                    case SyntaxKind.BigIntLiteral:
328                        return serializeLiteralOfLiteralTypeNode(operand as NumericLiteral | BigIntLiteral);
329                    default:
330                        return Debug.failBadSyntaxKind(operand);
331                }
332            }
333
334            case SyntaxKind.NumericLiteral:
335                return factory.createIdentifier("Number");
336
337            case SyntaxKind.BigIntLiteral:
338                return getGlobalConstructor("BigInt", ScriptTarget.ES2020);
339
340            case SyntaxKind.TrueKeyword:
341            case SyntaxKind.FalseKeyword:
342                return factory.createIdentifier("Boolean");
343
344            case SyntaxKind.NullKeyword:
345                return factory.createVoidZero();
346
347            default:
348                return Debug.failBadSyntaxKind(node);
349        }
350    }
351
352    function serializeUnionOrIntersectionConstituents(types: readonly TypeNode[], isIntersection: boolean): SerializedTypeNode {
353        // Note when updating logic here also update `getEntityNameForDecoratorMetadata` in checker.ts so that aliases can be marked as referenced
354        let serializedType: SerializedTypeNode | undefined;
355        for (let typeNode of types) {
356            typeNode = skipTypeParentheses(typeNode);
357            if (typeNode.kind === SyntaxKind.NeverKeyword) {
358                if (isIntersection) return factory.createVoidZero(); // Reduce to `never` in an intersection
359                continue; // Elide `never` in a union
360            }
361
362            if (typeNode.kind === SyntaxKind.UnknownKeyword) {
363                if (!isIntersection) return factory.createIdentifier("Object"); // Reduce to `unknown` in a union
364                continue; // Elide `unknown` in an intersection
365            }
366
367            if (typeNode.kind === SyntaxKind.AnyKeyword) {
368                return factory.createIdentifier("Object"); // Reduce to `any` in a union or intersection
369            }
370
371            if (!strictNullChecks && ((isLiteralTypeNode(typeNode) && typeNode.literal.kind === SyntaxKind.NullKeyword) || typeNode.kind === SyntaxKind.UndefinedKeyword)) {
372                continue; // Elide null and undefined from unions for metadata, just like what we did prior to the implementation of strict null checks
373            }
374
375            const serializedConstituent = serializeTypeNode(typeNode);
376            if (isIdentifier(serializedConstituent) && serializedConstituent.escapedText === "Object") {
377                // One of the individual is global object, return immediately
378                return serializedConstituent;
379            }
380
381            // If there exists union that is not `void 0` expression, check if the the common type is identifier.
382            // anything more complex and we will just default to Object
383            if (serializedType) {
384                // Different types
385                if (!equateSerializedTypeNodes(serializedType, serializedConstituent)) {
386                    return factory.createIdentifier("Object");
387                }
388            }
389            else {
390                // Initialize the union type
391                serializedType = serializedConstituent;
392            }
393        }
394
395        // If we were able to find common type, use it
396        return serializedType ?? (factory.createVoidZero()); // Fallback is only hit if all union constituents are null/undefined/never
397    }
398
399    function equateSerializedTypeNodes(left: Expression, right: Expression): boolean {
400        return (
401            // temp vars used in fallback
402            isGeneratedIdentifier(left) ? isGeneratedIdentifier(right) :
403
404            // entity names
405            isIdentifier(left) ? isIdentifier(right)
406                && left.escapedText === right.escapedText :
407
408            isPropertyAccessExpression(left) ? isPropertyAccessExpression(right)
409                && equateSerializedTypeNodes(left.expression, right.expression)
410                && equateSerializedTypeNodes(left.name, right.name) :
411
412            // `void 0`
413            isVoidExpression(left) ? isVoidExpression(right)
414                && isNumericLiteral(left.expression) && left.expression.text === "0"
415                && isNumericLiteral(right.expression) && right.expression.text === "0" :
416
417            // `"undefined"` or `"function"` in `typeof` checks
418            isStringLiteral(left) ? isStringLiteral(right)
419                && left.text === right.text :
420
421            // used in `typeof` checks for fallback
422            isTypeOfExpression(left) ? isTypeOfExpression(right)
423                && equateSerializedTypeNodes(left.expression, right.expression) :
424
425            // parens in `typeof` checks with temps
426            isParenthesizedExpression(left) ? isParenthesizedExpression(right)
427                && equateSerializedTypeNodes(left.expression, right.expression) :
428
429            // conditionals used in fallback
430            isConditionalExpression(left) ? isConditionalExpression(right)
431                && equateSerializedTypeNodes(left.condition, right.condition)
432                && equateSerializedTypeNodes(left.whenTrue, right.whenTrue)
433                && equateSerializedTypeNodes(left.whenFalse, right.whenFalse) :
434
435            // logical binary and assignments used in fallback
436            isBinaryExpression(left) ? isBinaryExpression(right)
437                && left.operatorToken.kind === right.operatorToken.kind
438                && equateSerializedTypeNodes(left.left, right.left)
439                && equateSerializedTypeNodes(left.right, right.right) :
440
441            false
442        );
443    }
444
445    /**
446     * Serializes a TypeReferenceNode to an appropriate JS constructor value for use with decorator type metadata.
447     * @param node The type reference node.
448     */
449    function serializeTypeReferenceNode(node: TypeReferenceNode): SerializedTypeNode {
450        const kind = resolver.getTypeReferenceSerializationKind(node.typeName, currentNameScope ?? currentLexicalScope);
451        switch (kind) {
452            case TypeReferenceSerializationKind.Unknown:
453                // From conditional type type reference that cannot be resolved is Similar to any or unknown
454                if (findAncestor(node, n => n.parent && isConditionalTypeNode(n.parent) && (n.parent.trueType === n || n.parent.falseType === n))) {
455                    return factory.createIdentifier("Object");
456                }
457
458                const serialized = serializeEntityNameAsExpressionFallback(node.typeName);
459                const temp = factory.createTempVariable(hoistVariableDeclaration);
460                return factory.createConditionalExpression(
461                    factory.createTypeCheck(factory.createAssignment(temp, serialized), "function"),
462                    /*questionToken*/ undefined,
463                    temp,
464                    /*colonToken*/ undefined,
465                    factory.createIdentifier("Object")
466                );
467
468            case TypeReferenceSerializationKind.TypeWithConstructSignatureAndValue:
469                return serializeEntityNameAsExpression(node.typeName);
470
471            case TypeReferenceSerializationKind.VoidNullableOrNeverType:
472                return factory.createVoidZero();
473
474            case TypeReferenceSerializationKind.BigIntLikeType:
475                return getGlobalConstructor("BigInt", ScriptTarget.ES2020);
476
477            case TypeReferenceSerializationKind.BooleanType:
478                return factory.createIdentifier("Boolean");
479
480            case TypeReferenceSerializationKind.NumberLikeType:
481                return factory.createIdentifier("Number");
482
483            case TypeReferenceSerializationKind.StringLikeType:
484                return factory.createIdentifier("String");
485
486            case TypeReferenceSerializationKind.ArrayLikeType:
487                return factory.createIdentifier("Array");
488
489            case TypeReferenceSerializationKind.ESSymbolType:
490                return getGlobalConstructor("Symbol", ScriptTarget.ES2015);
491
492            case TypeReferenceSerializationKind.TypeWithCallSignature:
493                return factory.createIdentifier("Function");
494
495            case TypeReferenceSerializationKind.Promise:
496                return factory.createIdentifier("Promise");
497
498            case TypeReferenceSerializationKind.ObjectType:
499                return factory.createIdentifier("Object");
500
501            default:
502                return Debug.assertNever(kind);
503        }
504    }
505
506    /**
507     * Produces an expression that results in `right` if `left` is not undefined at runtime:
508     *
509     * ```
510     * typeof left !== "undefined" && right
511     * ```
512     *
513     * We use `typeof L !== "undefined"` (rather than `L !== undefined`) since `L` may not be declared.
514     * It's acceptable for this expression to result in `false` at runtime, as the result is intended to be
515     * further checked by any containing expression.
516     */
517    function createCheckedValue(left: Expression, right: Expression) {
518        return factory.createLogicalAnd(
519            factory.createStrictInequality(factory.createTypeOfExpression(left), factory.createStringLiteral("undefined")),
520            right
521        );
522    }
523
524    /**
525     * Serializes an entity name which may not exist at runtime, but whose access shouldn't throw
526     * @param node The entity name to serialize.
527     */
528    function serializeEntityNameAsExpressionFallback(node: EntityName): BinaryExpression {
529        if (node.kind === SyntaxKind.Identifier) {
530            // A -> typeof A !== "undefined" && A
531            const copied = serializeEntityNameAsExpression(node);
532            return createCheckedValue(copied, copied);
533        }
534        if (node.left.kind === SyntaxKind.Identifier) {
535            // A.B -> typeof A !== "undefined" && A.B
536            return createCheckedValue(serializeEntityNameAsExpression(node.left), serializeEntityNameAsExpression(node));
537        }
538        // A.B.C -> typeof A !== "undefined" && (_a = A.B) !== void 0 && _a.C
539        const left = serializeEntityNameAsExpressionFallback(node.left);
540        const temp = factory.createTempVariable(hoistVariableDeclaration);
541        return factory.createLogicalAnd(
542            factory.createLogicalAnd(
543                left.left,
544                factory.createStrictInequality(factory.createAssignment(temp, left.right), factory.createVoidZero())
545            ),
546            factory.createPropertyAccessExpression(temp, node.right)
547        );
548    }
549
550    /**
551     * Serializes an entity name as an expression for decorator type metadata.
552     * @param node The entity name to serialize.
553     */
554    function serializeEntityNameAsExpression(node: EntityName): SerializedEntityName {
555        switch (node.kind) {
556            case SyntaxKind.Identifier:
557                // Create a clone of the name with a new parent, and treat it as if it were
558                // a source tree node for the purposes of the checker.
559                const name = setParent(setTextRange(parseNodeFactory.cloneNode(node), node), node.parent);
560                name.original = undefined;
561                setParent(name, getParseTreeNode(currentLexicalScope)); // ensure the parent is set to a parse tree node.
562                return name;
563
564            case SyntaxKind.QualifiedName:
565                return serializeQualifiedNameAsExpression(node);
566        }
567    }
568
569    /**
570     * Serializes an qualified name as an expression for decorator type metadata.
571     * @param node The qualified name to serialize.
572     */
573    function serializeQualifiedNameAsExpression(node: QualifiedName): SerializedEntityName {
574        return factory.createPropertyAccessExpression(serializeEntityNameAsExpression(node.left), node.right) as PropertyAccessEntityNameExpression;
575    }
576
577    function getGlobalConstructorWithFallback(name: string) {
578        return factory.createConditionalExpression(
579            factory.createTypeCheck(factory.createIdentifier(name), "function"),
580            /*questionToken*/ undefined,
581            factory.createIdentifier(name),
582            /*colonToken*/ undefined,
583            factory.createIdentifier("Object")
584        );
585    }
586
587    function getGlobalConstructor(name: string, minLanguageVersion: ScriptTarget): SerializedTypeNode {
588        return languageVersion < minLanguageVersion ?
589            getGlobalConstructorWithFallback(name) :
590            factory.createIdentifier(name);
591    }
592}