Skip to content

Commit b620cac

Browse files
committed
Fixes an issue when resolving a type name as an expression when emitting type metadata for decorators
1 parent 81711f9 commit b620cac

8 files changed

Lines changed: 140 additions & 7 deletions

File tree

src/compiler/checker.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14016,15 +14016,17 @@ namespace ts {
1401614016
return type.flags & TypeFlags.ObjectType && getSignaturesOfType(type, SignatureKind.Call).length > 0;
1401714017
}
1401814018

14019-
function getTypeReferenceSerializationKind(node: TypeReferenceNode): TypeReferenceSerializationKind {
14019+
function getTypeReferenceSerializationKind(typeName: EntityName): TypeReferenceSerializationKind {
1402014020
// Resolve the symbol as a value to ensure the type can be reached at runtime during emit.
14021-
let symbol = resolveEntityName(node.typeName, SymbolFlags.Value, /*ignoreErrors*/ true);
14022-
let constructorType = symbol ? getTypeOfSymbol(symbol) : undefined;
14021+
let valueSymbol = resolveEntityName(typeName, SymbolFlags.Value, /*ignoreErrors*/ true);
14022+
let constructorType = valueSymbol ? getTypeOfSymbol(valueSymbol) : undefined;
1402314023
if (constructorType && isConstructorType(constructorType)) {
1402414024
return TypeReferenceSerializationKind.TypeWithConstructSignatureAndValue;
1402514025
}
1402614026

14027-
let type = getTypeFromTypeNode(node);
14027+
// Resolve the symbol as a type so that we can provide a more useful hint for the type serializer.
14028+
let typeSymbol = resolveEntityName(typeName, SymbolFlags.Type, /*ignoreErrors*/ true);
14029+
let type = getDeclaredTypeOfSymbol(typeSymbol);
1402814030
if (type === unknownType) {
1402914031
return TypeReferenceSerializationKind.Unknown;
1403014032
}

src/compiler/emitter.ts

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4897,8 +4897,16 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
48974897

48984898
/** Serializes a TypeReferenceNode to an appropriate JS constructor value. Used by the __metadata decorator. */
48994899
function emitSerializedTypeReferenceNode(node: TypeReferenceNode) {
4900-
let typeName = node.typeName;
4901-
let result = resolver.getTypeReferenceSerializationKind(node);
4900+
let location: Node = node.parent;
4901+
while (isDeclaration(location) || isTypeNode(location)) {
4902+
location = location.parent;
4903+
}
4904+
4905+
// Clone the type name and parent it to a location outside of the current declaration.
4906+
let typeName = cloneEntityName(node.typeName);
4907+
typeName.parent = location;
4908+
4909+
let result = resolver.getTypeReferenceSerializationKind(typeName);
49024910
switch (result) {
49034911
case TypeReferenceSerializationKind.Unknown:
49044912
let temp = createAndRecordTempVariable(TempFlags.Auto);
@@ -5030,6 +5038,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
50305038
argumentsWritten++;
50315039
}
50325040
if (shouldEmitParamTypesMetadata(node)) {
5041+
debugger;
50335042
if (writeComma || argumentsWritten) {
50345043
write(", ");
50355044
}

src/compiler/types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1573,7 +1573,7 @@ namespace ts {
15731573
getConstantValue(node: EnumMember | PropertyAccessExpression | ElementAccessExpression): number;
15741574
getBlockScopedVariableId(node: Identifier): number;
15751575
getReferencedValueDeclaration(reference: Identifier): Declaration;
1576-
getTypeReferenceSerializationKind(node: TypeReferenceNode): TypeReferenceSerializationKind;
1576+
getTypeReferenceSerializationKind(typeName: EntityName): TypeReferenceSerializationKind;
15771577
}
15781578

15791579
export const enum SymbolFlags {

src/compiler/utilities.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1424,6 +1424,22 @@ namespace ts {
14241424
return isFunctionLike(n) || n.kind === SyntaxKind.ModuleDeclaration || n.kind === SyntaxKind.SourceFile;
14251425
}
14261426

1427+
export function cloneEntityName(node: EntityName): EntityName {
1428+
if (node.kind === SyntaxKind.Identifier) {
1429+
let clone = <Identifier>createSynthesizedNode(SyntaxKind.Identifier);
1430+
clone.text = (<Identifier>node).text;
1431+
return clone;
1432+
}
1433+
else {
1434+
let clone = <QualifiedName>createSynthesizedNode(SyntaxKind.QualifiedName);
1435+
clone.left = cloneEntityName((<QualifiedName>node).left);
1436+
clone.left.parent = clone;
1437+
clone.right = <Identifier>cloneEntityName((<QualifiedName>node).right);
1438+
clone.right.parent = clone;
1439+
return clone;
1440+
}
1441+
}
1442+
14271443
export function nodeIsSynthesized(node: Node): boolean {
14281444
return node.pos === -1;
14291445
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
//// [tests/cases/conformance/decorators/decoratorMetadata.ts] ////
2+
3+
//// [service.ts]
4+
export default class Service {
5+
}
6+
//// [component.ts]
7+
import Service from "./service";
8+
9+
declare var decorator: any;
10+
11+
@decorator
12+
class MyComponent {
13+
constructor(public Service: Service) {
14+
}
15+
}
16+
17+
//// [service.js]
18+
var Service = (function () {
19+
function Service() {
20+
}
21+
return Service;
22+
})();
23+
exports.default = Service;
24+
//// [component.js]
25+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
26+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") return Reflect.decorate(decorators, target, key, desc);
27+
switch (arguments.length) {
28+
case 2: return decorators.reduceRight(function(o, d) { return (d && d(o)) || o; }, target);
29+
case 3: return decorators.reduceRight(function(o, d) { return (d && d(target, key)), void 0; }, void 0);
30+
case 4: return decorators.reduceRight(function(o, d) { return (d && d(target, key, o)) || o; }, desc);
31+
}
32+
};
33+
var __metadata = (this && this.__metadata) || function (k, v) {
34+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
35+
};
36+
var MyComponent = (function () {
37+
function MyComponent(Service) {
38+
this.Service = Service;
39+
}
40+
MyComponent = __decorate([
41+
decorator,
42+
__metadata('design:paramtypes', [service_1.default])
43+
], MyComponent);
44+
return MyComponent;
45+
})();
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
=== tests/cases/conformance/decorators/service.ts ===
2+
export default class Service {
3+
>Service : Symbol(Service, Decl(service.ts, 0, 0))
4+
}
5+
=== tests/cases/conformance/decorators/component.ts ===
6+
import Service from "./service";
7+
>Service : Symbol(Service, Decl(component.ts, 0, 6))
8+
9+
declare var decorator: any;
10+
>decorator : Symbol(decorator, Decl(component.ts, 2, 11))
11+
12+
@decorator
13+
>decorator : Symbol(decorator, Decl(component.ts, 2, 11))
14+
15+
class MyComponent {
16+
>MyComponent : Symbol(MyComponent, Decl(component.ts, 2, 27))
17+
18+
constructor(public Service: Service) {
19+
>Service : Symbol(Service, Decl(component.ts, 6, 16))
20+
>Service : Symbol(Service, Decl(component.ts, 0, 6))
21+
}
22+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
=== tests/cases/conformance/decorators/service.ts ===
2+
export default class Service {
3+
>Service : Service
4+
}
5+
=== tests/cases/conformance/decorators/component.ts ===
6+
import Service from "./service";
7+
>Service : typeof Service
8+
9+
declare var decorator: any;
10+
>decorator : any
11+
12+
@decorator
13+
>decorator : any
14+
15+
class MyComponent {
16+
>MyComponent : MyComponent
17+
18+
constructor(public Service: Service) {
19+
>Service : Service
20+
>Service : Service
21+
}
22+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// @experimentalDecorators: true
2+
// @emitDecoratorMetadata: true
3+
// @target: es5
4+
// @module: commonjs
5+
// @filename: service.ts
6+
export default class Service {
7+
}
8+
// @filename: component.ts
9+
import Service from "./service";
10+
11+
declare var decorator: any;
12+
13+
@decorator
14+
class MyComponent {
15+
constructor(public Service: Service) {
16+
}
17+
}

0 commit comments

Comments
 (0)