From 73a829279ab9534802763bbda02aaf6816e08eaa Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Tue, 29 Nov 2016 12:42:17 -0800 Subject: [PATCH] Support union of non identifier serialized type node with null/undefined/never --- src/compiler/transformers/ts.ts | 91 ++++++++++--------- .../reference/metadataOfUnionWithNull.js | 39 +++++++- .../reference/metadataOfUnionWithNull.symbols | 33 +++++++ .../reference/metadataOfUnionWithNull.types | 37 ++++++++ .../cases/compiler/metadataOfUnionWithNull.ts | 15 +++ 5 files changed, 168 insertions(+), 47 deletions(-) diff --git a/src/compiler/transformers/ts.ts b/src/compiler/transformers/ts.ts index 366c94aa629..ecd2e2dfcfd 100644 --- a/src/compiler/transformers/ts.ts +++ b/src/compiler/transformers/ts.ts @@ -1637,6 +1637,8 @@ namespace ts { return createVoidZero(); } + type SerializedTypeNode = SerializedEntityNameAsExpression | VoidExpression | ConditionalExpression; + /** * Serializes a type node for use with decorator type metadata. * @@ -1655,7 +1657,7 @@ namespace ts { * * @param node The type node to serialize. */ - function serializeTypeNode(node: TypeNode): Expression { + function serializeTypeNode(node: TypeNode): SerializedTypeNode { if (node === undefined) { return createIdentifier("Object"); } @@ -1664,6 +1666,7 @@ namespace ts { case SyntaxKind.VoidKeyword: case SyntaxKind.UndefinedKeyword: case SyntaxKind.NullKeyword: + case SyntaxKind.NeverKeyword: return createVoidZero(); case SyntaxKind.ParenthesizedType: @@ -1715,45 +1718,8 @@ namespace ts { case SyntaxKind.IntersectionType: case SyntaxKind.UnionType: - { - const unionOrIntersection = node; - let serializedUnion: Identifier | VoidExpression; - for (const typeNode of unionOrIntersection.types) { - const serializedIndividual = serializeTypeNode(typeNode); + return serializeUnionOrIntersectionType(node); - if (isIdentifier(serializedIndividual)) { - // One of the individual is global object, return immediately - if (serializedIndividual.text === "Object") { - return serializedIndividual; - } - - // Different types - if (serializedUnion && isIdentifier(serializedUnion) && serializedUnion.text !== serializedIndividual.text) { - serializedUnion = undefined; - break; - } - - serializedUnion = serializedIndividual; - } - else if (isVoidExpression(serializedIndividual)) { - // If we dont have any other type already set, set the initial type - if (!serializedUnion) { - serializedUnion = serializedIndividual; - } - } - else { - // Non identifier and undefined/null - serializedUnion = undefined; - break; - } - } - - // If we were able to find common type - if (serializedUnion) { - return serializedUnion; - } - } - // Fallthrough case SyntaxKind.TypeQuery: case SyntaxKind.TypeOperator: case SyntaxKind.IndexedAccessType: @@ -1761,7 +1727,6 @@ namespace ts { case SyntaxKind.TypeLiteral: case SyntaxKind.AnyKeyword: case SyntaxKind.ThisType: - case SyntaxKind.NeverKeyword: break; default: @@ -1772,13 +1737,48 @@ namespace ts { return createIdentifier("Object"); } + function serializeUnionOrIntersectionType(node: UnionOrIntersectionTypeNode): SerializedTypeNode { + let serializedUnion: SerializedTypeNode; + for (const typeNode of node.types) { + const serializedIndividual = serializeTypeNode(typeNode); + + if (isVoidExpression(serializedIndividual)) { + // If we dont have any other type already set, set the initial type + if (!serializedUnion) { + serializedUnion = serializedIndividual; + } + } + else if (isIdentifier(serializedIndividual) && serializedIndividual.text === "Object") { + // One of the individual is global object, return immediately + return serializedIndividual; + } + // If there exists union that is not void 0 expression, check if the the common type is identifier. + // anything more complex and we will just default to Object + else if (serializedUnion && !isVoidExpression(serializedUnion)) { + // Different types + if (!isIdentifier(serializedUnion) || + !isIdentifier(serializedIndividual) || + serializedUnion.text !== serializedIndividual.text) { + return createIdentifier("Object"); + } + } + else { + // Initialize the union type + serializedUnion = serializedIndividual; + } + } + + // If we were able to find common type, use it + return serializedUnion; + } + /** * Serializes a TypeReferenceNode to an appropriate JS constructor value for use with * decorator type metadata. * * @param node The type reference node. */ - function serializeTypeReferenceNode(node: TypeReferenceNode) { + function serializeTypeReferenceNode(node: TypeReferenceNode): SerializedTypeNode { switch (resolver.getTypeReferenceSerializationKind(node.typeName, currentScope)) { case TypeReferenceSerializationKind.Unknown: const serialized = serializeEntityNameAsExpression(node.typeName, /*useFallback*/ true); @@ -1826,6 +1826,7 @@ namespace ts { } } + type SerializedEntityNameAsExpression = Identifier | BinaryExpression | PropertyAccessExpression; /** * Serializes an entity name as an expression for decorator type metadata. * @@ -1833,7 +1834,7 @@ namespace ts { * @param useFallback A value indicating whether to use logical operators to test for the * entity name at runtime. */ - function serializeEntityNameAsExpression(node: EntityName, useFallback: boolean): Expression { + function serializeEntityNameAsExpression(node: EntityName, useFallback: boolean): SerializedEntityNameAsExpression { switch (node.kind) { case SyntaxKind.Identifier: // Create a clone of the name with a new parent, and treat it as if it were @@ -1866,8 +1867,8 @@ namespace ts { * @param useFallback A value indicating whether to use logical operators to test for the * qualified name at runtime. */ - function serializeQualifiedNameAsExpression(node: QualifiedName, useFallback: boolean): Expression { - let left: Expression; + function serializeQualifiedNameAsExpression(node: QualifiedName, useFallback: boolean): PropertyAccessExpression { + let left: SerializedEntityNameAsExpression; if (node.left.kind === SyntaxKind.Identifier) { left = serializeEntityNameAsExpression(node.left, useFallback); } @@ -1892,7 +1893,7 @@ namespace ts { * Gets an expression that points to the global "Symbol" constructor at runtime if it is * available. */ - function getGlobalSymbolNameWithFallback(): Expression { + function getGlobalSymbolNameWithFallback(): ConditionalExpression { return createConditional( createTypeCheck(createIdentifier("Symbol"), "function"), createIdentifier("Symbol"), diff --git a/tests/baselines/reference/metadataOfUnionWithNull.js b/tests/baselines/reference/metadataOfUnionWithNull.js index 7b4b734b834..cc594ae326d 100644 --- a/tests/baselines/reference/metadataOfUnionWithNull.js +++ b/tests/baselines/reference/metadataOfUnionWithNull.js @@ -25,6 +25,21 @@ class B { @PropDeco d: undefined | null; + + @PropDeco + e: symbol | null; + + @PropDeco + f: symbol | A; + + @PropDeco + g: A | null; + + @PropDeco + h: null | B; + + @PropDeco + j: null | symbol; } //// [metadataOfUnionWithNull.js] @@ -54,7 +69,7 @@ __decorate([ ], B.prototype, "x"); __decorate([ PropDeco, - __metadata("design:type", Object) + __metadata("design:type", Boolean) ], B.prototype, "y"); __decorate([ PropDeco, @@ -66,7 +81,7 @@ __decorate([ ], B.prototype, "a"); __decorate([ PropDeco, - __metadata("design:type", Object) + __metadata("design:type", void 0) ], B.prototype, "b"); __decorate([ PropDeco, @@ -76,3 +91,23 @@ __decorate([ PropDeco, __metadata("design:type", void 0) ], B.prototype, "d"); +__decorate([ + PropDeco, + __metadata("design:type", typeof Symbol === "function" ? Symbol : Object) +], B.prototype, "e"); +__decorate([ + PropDeco, + __metadata("design:type", Object) +], B.prototype, "f"); +__decorate([ + PropDeco, + __metadata("design:type", A) +], B.prototype, "g"); +__decorate([ + PropDeco, + __metadata("design:type", B) +], B.prototype, "h"); +__decorate([ + PropDeco, + __metadata("design:type", typeof Symbol === "function" ? Symbol : Object) +], B.prototype, "j"); diff --git a/tests/baselines/reference/metadataOfUnionWithNull.symbols b/tests/baselines/reference/metadataOfUnionWithNull.symbols index 13533a26699..13316bc7cf1 100644 --- a/tests/baselines/reference/metadataOfUnionWithNull.symbols +++ b/tests/baselines/reference/metadataOfUnionWithNull.symbols @@ -53,4 +53,37 @@ class B { d: undefined | null; >d : Symbol(B.d, Decl(metadataOfUnionWithNull.ts, 22, 17)) + + @PropDeco +>PropDeco : Symbol(PropDeco, Decl(metadataOfUnionWithNull.ts, 0, 0)) + + e: symbol | null; +>e : Symbol(B.e, Decl(metadataOfUnionWithNull.ts, 25, 24)) + + @PropDeco +>PropDeco : Symbol(PropDeco, Decl(metadataOfUnionWithNull.ts, 0, 0)) + + f: symbol | A; +>f : Symbol(B.f, Decl(metadataOfUnionWithNull.ts, 28, 21)) +>A : Symbol(A, Decl(metadataOfUnionWithNull.ts, 0, 63)) + + @PropDeco +>PropDeco : Symbol(PropDeco, Decl(metadataOfUnionWithNull.ts, 0, 0)) + + g: A | null; +>g : Symbol(B.g, Decl(metadataOfUnionWithNull.ts, 31, 18)) +>A : Symbol(A, Decl(metadataOfUnionWithNull.ts, 0, 63)) + + @PropDeco +>PropDeco : Symbol(PropDeco, Decl(metadataOfUnionWithNull.ts, 0, 0)) + + h: null | B; +>h : Symbol(B.h, Decl(metadataOfUnionWithNull.ts, 34, 16)) +>B : Symbol(B, Decl(metadataOfUnionWithNull.ts, 3, 1)) + + @PropDeco +>PropDeco : Symbol(PropDeco, Decl(metadataOfUnionWithNull.ts, 0, 0)) + + j: null | symbol; +>j : Symbol(B.j, Decl(metadataOfUnionWithNull.ts, 37, 16)) } diff --git a/tests/baselines/reference/metadataOfUnionWithNull.types b/tests/baselines/reference/metadataOfUnionWithNull.types index e6037e16cb3..04a5b162d4c 100644 --- a/tests/baselines/reference/metadataOfUnionWithNull.types +++ b/tests/baselines/reference/metadataOfUnionWithNull.types @@ -56,5 +56,42 @@ class B { d: undefined | null; >d : null +>null : null + + @PropDeco +>PropDeco : (target: Object, propKey: string | symbol) => void + + e: symbol | null; +>e : symbol +>null : null + + @PropDeco +>PropDeco : (target: Object, propKey: string | symbol) => void + + f: symbol | A; +>f : symbol | A +>A : A + + @PropDeco +>PropDeco : (target: Object, propKey: string | symbol) => void + + g: A | null; +>g : A +>A : A +>null : null + + @PropDeco +>PropDeco : (target: Object, propKey: string | symbol) => void + + h: null | B; +>h : B +>null : null +>B : B + + @PropDeco +>PropDeco : (target: Object, propKey: string | symbol) => void + + j: null | symbol; +>j : symbol >null : null } diff --git a/tests/cases/compiler/metadataOfUnionWithNull.ts b/tests/cases/compiler/metadataOfUnionWithNull.ts index 475024229fc..bb4d93189fc 100644 --- a/tests/cases/compiler/metadataOfUnionWithNull.ts +++ b/tests/cases/compiler/metadataOfUnionWithNull.ts @@ -26,4 +26,19 @@ class B { @PropDeco d: undefined | null; + + @PropDeco + e: symbol | null; + + @PropDeco + f: symbol | A; + + @PropDeco + g: A | null; + + @PropDeco + h: null | B; + + @PropDeco + j: null | symbol; } \ No newline at end of file