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}