Fix decorator design:types emit for type variables.

Previously, TypeScript would resolve the reified types for the
`design:types` decorator emit in the regular `currentScope`. That scope
does not include class declaration bodies.

However when reifying types, class declarations do introduce a new scope
for any `TypeVariable`s declared on them. Because TS resolved the
EntityName for such types against the parent scope (e.g. the source
file), not the class scope, TypeScript would either fail to resolve the type (giving `TypeReferenceSerializationKind.Unknown`), or
incorrectly resolve to a different, accidentally matching symbol in the outer scope (giving `TypeWithConstructSignatureAndValue`).

This would result in an emit referencing an undeclared symbol, or
mis-referencing the wrong symbol.

    __metadata("design:type", typeof (_a = typeof TypeVariable !== "undefined" && TypeVariable) === "function" && _a || Object)
    __metadata("design:type", TypeVariable)

This change special cases `currentScope` for
`serializeTypeReferenceNode` to use a class scope, if present. This
changes the emit for a `TypeVariable` back to `Object`:

    __metadata("design:type", Object)
This commit is contained in:
Martin Probst
2018-06-25 17:24:05 +02:00
parent 22d33d2292
commit 55c3ec3e94
16 changed files with 246 additions and 1 deletions
+10 -1
View File
@@ -1969,7 +1969,16 @@ namespace ts {
* @param node The type reference node.
*/
function serializeTypeReferenceNode(node: TypeReferenceNode): SerializedTypeNode {
const kind = resolver.getTypeReferenceSerializationKind(node.typeName, currentScope);
// node might be a reference to type variable, which can be scoped to a class declaration, in addition to the regular
// TypeScript scopes. Walk up the AST to find the next class and use that as the lookup scope.
let scope: Node = node;
while (scope.parent && scope !== currentScope) {
scope = scope.parent;
if (isClassDeclaration(scope)) {
break;
}
}
const kind = resolver.getTypeReferenceSerializationKind(node.typeName, scope);
switch (kind) {
case TypeReferenceSerializationKind.Unknown:
const serialized = serializeEntityNameAsExpression(node.typeName, /*useFallback*/ true);
@@ -0,0 +1,11 @@
tests/cases/compiler/decoratorMetadataGenericTypeVariable.ts(2,4): error TS2304: Cannot find name 'Decorate'.
==== tests/cases/compiler/decoratorMetadataGenericTypeVariable.ts (1 errors) ====
export class C<TypeVariable> {
@Decorate
~~~~~~~~
!!! error TS2304: Cannot find name 'Decorate'.
member: TypeVariable;
}
@@ -0,0 +1,29 @@
//// [decoratorMetadataGenericTypeVariable.ts]
export class C<TypeVariable> {
@Decorate
member: TypeVariable;
}
//// [decoratorMetadataGenericTypeVariable.js]
"use strict";
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);
};
exports.__esModule = true;
var C = /** @class */ (function () {
function C() {
}
__decorate([
Decorate,
__metadata("design:type", Object)
], C.prototype, "member");
return C;
}());
exports.C = C;
@@ -0,0 +1,11 @@
=== tests/cases/compiler/decoratorMetadataGenericTypeVariable.ts ===
export class C<TypeVariable> {
>C : Symbol(C, Decl(decoratorMetadataGenericTypeVariable.ts, 0, 0))
>TypeVariable : Symbol(TypeVariable, Decl(decoratorMetadataGenericTypeVariable.ts, 0, 15))
@Decorate
member: TypeVariable;
>member : Symbol(C.member, Decl(decoratorMetadataGenericTypeVariable.ts, 0, 30))
>TypeVariable : Symbol(TypeVariable, Decl(decoratorMetadataGenericTypeVariable.ts, 0, 15))
}
@@ -0,0 +1,13 @@
=== tests/cases/compiler/decoratorMetadataGenericTypeVariable.ts ===
export class C<TypeVariable> {
>C : C<TypeVariable>
>TypeVariable : TypeVariable
@Decorate
>Decorate : any
member: TypeVariable;
>member : TypeVariable
>TypeVariable : TypeVariable
}
@@ -0,0 +1,11 @@
tests/cases/compiler/decoratorMetadataGenericTypeVariableDefault.ts(2,4): error TS2304: Cannot find name 'Decorate'.
==== tests/cases/compiler/decoratorMetadataGenericTypeVariableDefault.ts (1 errors) ====
export class C<TypeVariable = string> {
@Decorate
~~~~~~~~
!!! error TS2304: Cannot find name 'Decorate'.
member: TypeVariable;
}
@@ -0,0 +1,29 @@
//// [decoratorMetadataGenericTypeVariableDefault.ts]
export class C<TypeVariable = string> {
@Decorate
member: TypeVariable;
}
//// [decoratorMetadataGenericTypeVariableDefault.js]
"use strict";
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);
};
exports.__esModule = true;
var C = /** @class */ (function () {
function C() {
}
__decorate([
Decorate,
__metadata("design:type", Object)
], C.prototype, "member");
return C;
}());
exports.C = C;
@@ -0,0 +1,11 @@
=== tests/cases/compiler/decoratorMetadataGenericTypeVariableDefault.ts ===
export class C<TypeVariable = string> {
>C : Symbol(C, Decl(decoratorMetadataGenericTypeVariableDefault.ts, 0, 0))
>TypeVariable : Symbol(TypeVariable, Decl(decoratorMetadataGenericTypeVariableDefault.ts, 0, 15))
@Decorate
member: TypeVariable;
>member : Symbol(C.member, Decl(decoratorMetadataGenericTypeVariableDefault.ts, 0, 39))
>TypeVariable : Symbol(TypeVariable, Decl(decoratorMetadataGenericTypeVariableDefault.ts, 0, 15))
}
@@ -0,0 +1,13 @@
=== tests/cases/compiler/decoratorMetadataGenericTypeVariableDefault.ts ===
export class C<TypeVariable = string> {
>C : C<TypeVariable>
>TypeVariable : TypeVariable
@Decorate
>Decorate : any
member: TypeVariable;
>member : TypeVariable
>TypeVariable : TypeVariable
}
@@ -0,0 +1,14 @@
tests/cases/compiler/decoratorMetadataGenericTypeVariableInScope.ts(5,4): error TS2304: Cannot find name 'Decorate'.
==== tests/cases/compiler/decoratorMetadataGenericTypeVariableInScope.ts (1 errors) ====
// Unused, but could collide with the named type argument below.
class TypeVariable {}
export class C<TypeVariable> {
@Decorate
~~~~~~~~
!!! error TS2304: Cannot find name 'Decorate'.
member: TypeVariable;
}
@@ -0,0 +1,38 @@
//// [decoratorMetadataGenericTypeVariableInScope.ts]
// Unused, but could collide with the named type argument below.
class TypeVariable {}
export class C<TypeVariable> {
@Decorate
member: TypeVariable;
}
//// [decoratorMetadataGenericTypeVariableInScope.js]
"use strict";
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);
};
exports.__esModule = true;
// Unused, but could collide with the named type argument below.
var TypeVariable = /** @class */ (function () {
function TypeVariable() {
}
return TypeVariable;
}());
var C = /** @class */ (function () {
function C() {
}
__decorate([
Decorate,
__metadata("design:type", Object)
], C.prototype, "member");
return C;
}());
exports.C = C;
@@ -0,0 +1,15 @@
=== tests/cases/compiler/decoratorMetadataGenericTypeVariableInScope.ts ===
// Unused, but could collide with the named type argument below.
class TypeVariable {}
>TypeVariable : Symbol(TypeVariable, Decl(decoratorMetadataGenericTypeVariableInScope.ts, 0, 0))
export class C<TypeVariable> {
>C : Symbol(C, Decl(decoratorMetadataGenericTypeVariableInScope.ts, 1, 21))
>TypeVariable : Symbol(TypeVariable, Decl(decoratorMetadataGenericTypeVariableInScope.ts, 3, 15))
@Decorate
member: TypeVariable;
>member : Symbol(C.member, Decl(decoratorMetadataGenericTypeVariableInScope.ts, 3, 30))
>TypeVariable : Symbol(TypeVariable, Decl(decoratorMetadataGenericTypeVariableInScope.ts, 3, 15))
}
@@ -0,0 +1,17 @@
=== tests/cases/compiler/decoratorMetadataGenericTypeVariableInScope.ts ===
// Unused, but could collide with the named type argument below.
class TypeVariable {}
>TypeVariable : TypeVariable
export class C<TypeVariable> {
>C : C<TypeVariable>
>TypeVariable : TypeVariable
@Decorate
>Decorate : any
member: TypeVariable;
>member : TypeVariable
>TypeVariable : TypeVariable
}
@@ -0,0 +1,7 @@
// @experimentalDecorators: true
// @emitDecoratorMetadata: true
export class C<TypeVariable> {
@Decorate
member: TypeVariable;
}
@@ -0,0 +1,7 @@
// @experimentalDecorators: true
// @emitDecoratorMetadata: true
export class C<TypeVariable = string> {
@Decorate
member: TypeVariable;
}
@@ -0,0 +1,10 @@
// @experimentalDecorators: true
// @emitDecoratorMetadata: true
// Unused, but could collide with the named type argument below.
class TypeVariable {}
export class C<TypeVariable> {
@Decorate
member: TypeVariable;
}