From 59403796c70ec51d1f40c398befa50768ca7b289 Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Tue, 29 Nov 2016 12:42:17 -0800 Subject: [PATCH] Add serialization of typenode for null/undefined/never as part of metadata type Fixes #12684 and #11933 --- src/compiler/transformers/ts.ts | 45 +++++++---- src/compiler/utilities.ts | 4 + .../reference/metadataOfUnionWithNull.js | 78 +++++++++++++++++++ .../reference/metadataOfUnionWithNull.symbols | 56 +++++++++++++ .../reference/metadataOfUnionWithNull.types | 60 ++++++++++++++ .../cases/compiler/metadataOfUnionWithNull.ts | 29 +++++++ 6 files changed, 255 insertions(+), 17 deletions(-) create mode 100644 tests/baselines/reference/metadataOfUnionWithNull.js create mode 100644 tests/baselines/reference/metadataOfUnionWithNull.symbols create mode 100644 tests/baselines/reference/metadataOfUnionWithNull.types create mode 100644 tests/cases/compiler/metadataOfUnionWithNull.ts diff --git a/src/compiler/transformers/ts.ts b/src/compiler/transformers/ts.ts index 877131b369c..366c94aa629 100644 --- a/src/compiler/transformers/ts.ts +++ b/src/compiler/transformers/ts.ts @@ -1662,6 +1662,8 @@ namespace ts { switch (node.kind) { case SyntaxKind.VoidKeyword: + case SyntaxKind.UndefinedKeyword: + case SyntaxKind.NullKeyword: return createVoidZero(); case SyntaxKind.ParenthesizedType: @@ -1715,27 +1717,35 @@ namespace ts { case SyntaxKind.UnionType: { const unionOrIntersection = node; - let serializedUnion: Identifier; + let serializedUnion: Identifier | VoidExpression; for (const typeNode of unionOrIntersection.types) { - const serializedIndividual = serializeTypeNode(typeNode) as Identifier; - // Non identifier - if (serializedIndividual.kind !== SyntaxKind.Identifier) { + const serializedIndividual = serializeTypeNode(typeNode); + + 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; } - - // One of the individual is global object, return immediately - if (serializedIndividual.text === "Object") { - return serializedIndividual; - } - - // Different types - if (serializedUnion && serializedUnion.text !== serializedIndividual.text) { - serializedUnion = undefined; - break; - } - - serializedUnion = serializedIndividual; } // If we were able to find common type @@ -1751,6 +1761,7 @@ namespace ts { case SyntaxKind.TypeLiteral: case SyntaxKind.AnyKeyword: case SyntaxKind.ThisType: + case SyntaxKind.NeverKeyword: break; default: diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 6f491f6708f..0914b0d0c24 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -3581,6 +3581,10 @@ namespace ts { return node.kind === SyntaxKind.Identifier; } + export function isVoidExpression(node: Node): node is VoidExpression { + return node.kind === SyntaxKind.VoidExpression; + } + export function isGeneratedIdentifier(node: Node): node is GeneratedIdentifier { // Using `>` here catches both `GeneratedIdentifierKind.None` and `undefined`. return isIdentifier(node) && node.autoGenerateKind > GeneratedIdentifierKind.None; diff --git a/tests/baselines/reference/metadataOfUnionWithNull.js b/tests/baselines/reference/metadataOfUnionWithNull.js new file mode 100644 index 00000000000..7b4b734b834 --- /dev/null +++ b/tests/baselines/reference/metadataOfUnionWithNull.js @@ -0,0 +1,78 @@ +//// [metadataOfUnionWithNull.ts] +function PropDeco(target: Object, propKey: string | symbol) { } + +class A { +} + +class B { + @PropDeco + x: "foo" | null; + + @PropDeco + y: true | never; + + @PropDeco + z: "foo" | undefined; + + @PropDeco + a: null; + + @PropDeco + b: never; + + @PropDeco + c: undefined; + + @PropDeco + d: undefined | null; +} + +//// [metadataOfUnionWithNull.js] +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +function PropDeco(target, propKey) { } +var A = (function () { + function A() { + } + return A; +}()); +var B = (function () { + function B() { + } + return B; +}()); +__decorate([ + PropDeco, + __metadata("design:type", String) +], B.prototype, "x"); +__decorate([ + PropDeco, + __metadata("design:type", Object) +], B.prototype, "y"); +__decorate([ + PropDeco, + __metadata("design:type", String) +], B.prototype, "z"); +__decorate([ + PropDeco, + __metadata("design:type", void 0) +], B.prototype, "a"); +__decorate([ + PropDeco, + __metadata("design:type", Object) +], B.prototype, "b"); +__decorate([ + PropDeco, + __metadata("design:type", void 0) +], B.prototype, "c"); +__decorate([ + PropDeco, + __metadata("design:type", void 0) +], B.prototype, "d"); diff --git a/tests/baselines/reference/metadataOfUnionWithNull.symbols b/tests/baselines/reference/metadataOfUnionWithNull.symbols new file mode 100644 index 00000000000..13533a26699 --- /dev/null +++ b/tests/baselines/reference/metadataOfUnionWithNull.symbols @@ -0,0 +1,56 @@ +=== tests/cases/compiler/metadataOfUnionWithNull.ts === +function PropDeco(target: Object, propKey: string | symbol) { } +>PropDeco : Symbol(PropDeco, Decl(metadataOfUnionWithNull.ts, 0, 0)) +>target : Symbol(target, Decl(metadataOfUnionWithNull.ts, 0, 18)) +>Object : Symbol(Object, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) +>propKey : Symbol(propKey, Decl(metadataOfUnionWithNull.ts, 0, 33)) + +class A { +>A : Symbol(A, Decl(metadataOfUnionWithNull.ts, 0, 63)) +} + +class B { +>B : Symbol(B, Decl(metadataOfUnionWithNull.ts, 3, 1)) + + @PropDeco +>PropDeco : Symbol(PropDeco, Decl(metadataOfUnionWithNull.ts, 0, 0)) + + x: "foo" | null; +>x : Symbol(B.x, Decl(metadataOfUnionWithNull.ts, 5, 9)) + + @PropDeco +>PropDeco : Symbol(PropDeco, Decl(metadataOfUnionWithNull.ts, 0, 0)) + + y: true | never; +>y : Symbol(B.y, Decl(metadataOfUnionWithNull.ts, 7, 20)) + + @PropDeco +>PropDeco : Symbol(PropDeco, Decl(metadataOfUnionWithNull.ts, 0, 0)) + + z: "foo" | undefined; +>z : Symbol(B.z, Decl(metadataOfUnionWithNull.ts, 10, 20)) + + @PropDeco +>PropDeco : Symbol(PropDeco, Decl(metadataOfUnionWithNull.ts, 0, 0)) + + a: null; +>a : Symbol(B.a, Decl(metadataOfUnionWithNull.ts, 13, 25)) + + @PropDeco +>PropDeco : Symbol(PropDeco, Decl(metadataOfUnionWithNull.ts, 0, 0)) + + b: never; +>b : Symbol(B.b, Decl(metadataOfUnionWithNull.ts, 16, 12)) + + @PropDeco +>PropDeco : Symbol(PropDeco, Decl(metadataOfUnionWithNull.ts, 0, 0)) + + c: undefined; +>c : Symbol(B.c, Decl(metadataOfUnionWithNull.ts, 19, 13)) + + @PropDeco +>PropDeco : Symbol(PropDeco, Decl(metadataOfUnionWithNull.ts, 0, 0)) + + d: undefined | null; +>d : Symbol(B.d, Decl(metadataOfUnionWithNull.ts, 22, 17)) +} diff --git a/tests/baselines/reference/metadataOfUnionWithNull.types b/tests/baselines/reference/metadataOfUnionWithNull.types new file mode 100644 index 00000000000..e6037e16cb3 --- /dev/null +++ b/tests/baselines/reference/metadataOfUnionWithNull.types @@ -0,0 +1,60 @@ +=== tests/cases/compiler/metadataOfUnionWithNull.ts === +function PropDeco(target: Object, propKey: string | symbol) { } +>PropDeco : (target: Object, propKey: string | symbol) => void +>target : Object +>Object : Object +>propKey : string | symbol + +class A { +>A : A +} + +class B { +>B : B + + @PropDeco +>PropDeco : (target: Object, propKey: string | symbol) => void + + x: "foo" | null; +>x : "foo" +>null : null + + @PropDeco +>PropDeco : (target: Object, propKey: string | symbol) => void + + y: true | never; +>y : true +>true : true + + @PropDeco +>PropDeco : (target: Object, propKey: string | symbol) => void + + z: "foo" | undefined; +>z : "foo" + + @PropDeco +>PropDeco : (target: Object, propKey: string | symbol) => void + + a: null; +>a : null +>null : null + + @PropDeco +>PropDeco : (target: Object, propKey: string | symbol) => void + + b: never; +>b : never + + @PropDeco +>PropDeco : (target: Object, propKey: string | symbol) => void + + c: undefined; +>c : undefined + + @PropDeco +>PropDeco : (target: Object, propKey: string | symbol) => void + + d: undefined | null; +>d : null +>null : null +} diff --git a/tests/cases/compiler/metadataOfUnionWithNull.ts b/tests/cases/compiler/metadataOfUnionWithNull.ts new file mode 100644 index 00000000000..475024229fc --- /dev/null +++ b/tests/cases/compiler/metadataOfUnionWithNull.ts @@ -0,0 +1,29 @@ +// @experimentalDecorators: true +// @emitDecoratorMetadata: true +function PropDeco(target: Object, propKey: string | symbol) { } + +class A { +} + +class B { + @PropDeco + x: "foo" | null; + + @PropDeco + y: true | never; + + @PropDeco + z: "foo" | undefined; + + @PropDeco + a: null; + + @PropDeco + b: never; + + @PropDeco + c: undefined; + + @PropDeco + d: undefined | null; +} \ No newline at end of file