diff --git a/src/compiler/transformers/ts.ts b/src/compiler/transformers/ts.ts index e67918fd696..ba4b5fcdf52 100644 --- a/src/compiler/transformers/ts.ts +++ b/src/compiler/transformers/ts.ts @@ -45,6 +45,7 @@ namespace ts { const resolver = context.getEmitResolver(); const compilerOptions = context.getCompilerOptions(); + const strictNullChecks = typeof compilerOptions.strictNullChecks === "undefined" ? compilerOptions.strict : compilerOptions.strictNullChecks; const languageVersion = getEmitScriptTarget(compilerOptions); const moduleKind = getEmitModuleKind(compilerOptions); @@ -1869,7 +1870,16 @@ namespace ts { // Note when updating logic here also update getEntityNameForDecoratorMetadata // so that aliases can be marked as referenced let serializedUnion: SerializedTypeNode; - for (const typeNode of node.types) { + for (let typeNode of node.types) { + while (typeNode.kind === SyntaxKind.ParenthesizedType) { + typeNode = (typeNode as ParenthesizedTypeNode).type; // Skip parens if need be + } + if (typeNode.kind === SyntaxKind.NeverKeyword) { + continue; // Always elide `never` from the union/intersection if possible + } + if (!strictNullChecks && (typeNode.kind === SyntaxKind.NullKeyword || typeNode.kind === SyntaxKind.UndefinedKeyword)) { + continue; // Elide null and undefined from unions for metadata, just like what we did prior to the implementation of strict null checks + } const serializedIndividual = serializeTypeNode(typeNode); if (isIdentifier(serializedIndividual) && serializedIndividual.escapedText === "Object") { @@ -1893,7 +1903,7 @@ namespace ts { } // If we were able to find common type, use it - return serializedUnion; + return serializedUnion || createVoidZero(); // Fallback is only hit if all union constituients are null/undefined/never } /** diff --git a/tests/baselines/reference/decoratorMetadataNoStrictNull.js b/tests/baselines/reference/decoratorMetadataNoStrictNull.js new file mode 100644 index 00000000000..dada68f0960 --- /dev/null +++ b/tests/baselines/reference/decoratorMetadataNoStrictNull.js @@ -0,0 +1,32 @@ +//// [decoratorMetadataNoStrictNull.ts] +const dec = (obj: {}, prop: string) => undefined + +class Foo { + @dec public foo: string | null; + @dec public bar: string; +} + +//// [decoratorMetadataNoStrictNull.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); +}; +var dec = function (obj, prop) { return undefined; }; +var Foo = /** @class */ (function () { + function Foo() { + } + __decorate([ + dec, + __metadata("design:type", String) + ], Foo.prototype, "foo"); + __decorate([ + dec, + __metadata("design:type", String) + ], Foo.prototype, "bar"); + return Foo; +}()); diff --git a/tests/baselines/reference/decoratorMetadataNoStrictNull.symbols b/tests/baselines/reference/decoratorMetadataNoStrictNull.symbols new file mode 100644 index 00000000000..32a6d08f024 --- /dev/null +++ b/tests/baselines/reference/decoratorMetadataNoStrictNull.symbols @@ -0,0 +1,18 @@ +=== tests/cases/compiler/decoratorMetadataNoStrictNull.ts === +const dec = (obj: {}, prop: string) => undefined +>dec : Symbol(dec, Decl(decoratorMetadataNoStrictNull.ts, 0, 5)) +>obj : Symbol(obj, Decl(decoratorMetadataNoStrictNull.ts, 0, 13)) +>prop : Symbol(prop, Decl(decoratorMetadataNoStrictNull.ts, 0, 21)) +>undefined : Symbol(undefined) + +class Foo { +>Foo : Symbol(Foo, Decl(decoratorMetadataNoStrictNull.ts, 0, 48)) + + @dec public foo: string | null; +>dec : Symbol(dec, Decl(decoratorMetadataNoStrictNull.ts, 0, 5)) +>foo : Symbol(Foo.foo, Decl(decoratorMetadataNoStrictNull.ts, 2, 11)) + + @dec public bar: string; +>dec : Symbol(dec, Decl(decoratorMetadataNoStrictNull.ts, 0, 5)) +>bar : Symbol(Foo.bar, Decl(decoratorMetadataNoStrictNull.ts, 3, 33)) +} diff --git a/tests/baselines/reference/decoratorMetadataNoStrictNull.types b/tests/baselines/reference/decoratorMetadataNoStrictNull.types new file mode 100644 index 00000000000..981efe6e50b --- /dev/null +++ b/tests/baselines/reference/decoratorMetadataNoStrictNull.types @@ -0,0 +1,20 @@ +=== tests/cases/compiler/decoratorMetadataNoStrictNull.ts === +const dec = (obj: {}, prop: string) => undefined +>dec : (obj: {}, prop: string) => any +>(obj: {}, prop: string) => undefined : (obj: {}, prop: string) => any +>obj : {} +>prop : string +>undefined : undefined + +class Foo { +>Foo : Foo + + @dec public foo: string | null; +>dec : (obj: {}, prop: string) => any +>foo : string +>null : null + + @dec public bar: string; +>dec : (obj: {}, prop: string) => any +>bar : string +} diff --git a/tests/baselines/reference/metadataOfClassFromAlias.js b/tests/baselines/reference/metadataOfClassFromAlias.js index 307702cd7bf..77ae5c33898 100644 --- a/tests/baselines/reference/metadataOfClassFromAlias.js +++ b/tests/baselines/reference/metadataOfClassFromAlias.js @@ -43,7 +43,7 @@ var ClassA = /** @class */ (function () { } __decorate([ annotation(), - __metadata("design:type", Object) + __metadata("design:type", auxiliry_1.SomeClass) ], ClassA.prototype, "array", void 0); return ClassA; }()); diff --git a/tests/baselines/reference/metadataOfUnionWithNull.js b/tests/baselines/reference/metadataOfUnionWithNull.js index 80d24709b73..7bf9aeb8650 100644 --- a/tests/baselines/reference/metadataOfUnionWithNull.js +++ b/tests/baselines/reference/metadataOfUnionWithNull.js @@ -63,15 +63,15 @@ var B = /** @class */ (function () { } __decorate([ PropDeco, - __metadata("design:type", Object) + __metadata("design:type", String) ], B.prototype, "x"); __decorate([ PropDeco, - __metadata("design:type", Object) + __metadata("design:type", Boolean) ], B.prototype, "y"); __decorate([ PropDeco, - __metadata("design:type", Object) + __metadata("design:type", String) ], B.prototype, "z"); __decorate([ PropDeco, @@ -87,11 +87,11 @@ var B = /** @class */ (function () { ], B.prototype, "c"); __decorate([ PropDeco, - __metadata("design:type", Object) + __metadata("design:type", void 0) ], B.prototype, "d"); __decorate([ PropDeco, - __metadata("design:type", Object) + __metadata("design:type", typeof Symbol === "function" ? Symbol : Object) ], B.prototype, "e"); __decorate([ PropDeco, @@ -99,15 +99,15 @@ var B = /** @class */ (function () { ], B.prototype, "f"); __decorate([ PropDeco, - __metadata("design:type", Object) + __metadata("design:type", A) ], B.prototype, "g"); __decorate([ PropDeco, - __metadata("design:type", Object) + __metadata("design:type", B) ], B.prototype, "h"); __decorate([ PropDeco, - __metadata("design:type", Object) + __metadata("design:type", typeof Symbol === "function" ? Symbol : Object) ], B.prototype, "j"); return B; }()); diff --git a/tests/cases/compiler/decoratorMetadataNoStrictNull.ts b/tests/cases/compiler/decoratorMetadataNoStrictNull.ts new file mode 100644 index 00000000000..4ac608ebd74 --- /dev/null +++ b/tests/cases/compiler/decoratorMetadataNoStrictNull.ts @@ -0,0 +1,8 @@ +// @experimentalDecorators: true +// @emitDecoratorMetadata: true +const dec = (obj: {}, prop: string) => undefined + +class Foo { + @dec public foo: string | null; + @dec public bar: string; +} \ No newline at end of file