Skip to content

Commit 96aca4c

Browse files
authored
Merge pull request microsoft#13540 from Microsoft/metadataReferences
Mark as referenced aliases in Union that will get emitted as part of decorator metadata
2 parents 7fe1aba + 0eaa8eb commit 96aca4c

12 files changed

+326
-26
lines changed

src/compiler/checker.ts

Lines changed: 64 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18717,7 +18717,10 @@ namespace ts {
1871718717
* marked as referenced to prevent import elision.
1871818718
*/
1871918719
function markTypeNodeAsReferenced(node: TypeNode) {
18720-
const typeName = node && getEntityNameFromTypeNode(node);
18720+
markEntityNameOrEntityExpressionAsReference(node && getEntityNameFromTypeNode(node));
18721+
}
18722+
18723+
function markEntityNameOrEntityExpressionAsReference(typeName: EntityNameOrEntityNameExpression) {
1872118724
const rootName = typeName && getFirstIdentifier(typeName);
1872218725
const rootSymbol = rootName && resolveName(rootName, rootName.text, (typeName.kind === SyntaxKind.Identifier ? SymbolFlags.Type : SymbolFlags.Namespace) | SymbolFlags.Alias, /*nameNotFoundMessage*/ undefined, /*nameArg*/ undefined);
1872318726
if (rootSymbol
@@ -18728,6 +18731,61 @@ namespace ts {
1872818731
}
1872918732
}
1873018733

18734+
/**
18735+
* This function marks the type used for metadata decorator as referenced if it is import
18736+
* from external module.
18737+
* This is different from markTypeNodeAsReferenced because it tries to simplify type nodes in
18738+
* union and intersection type
18739+
* @param node
18740+
*/
18741+
function markDecoratorMedataDataTypeNodeAsReferenced(node: TypeNode): void {
18742+
const entityName = getEntityNameForDecoratorMetadata(node);
18743+
if (entityName && isEntityName(entityName)) {
18744+
markEntityNameOrEntityExpressionAsReference(entityName);
18745+
}
18746+
}
18747+
18748+
function getEntityNameForDecoratorMetadata(node: TypeNode): EntityName {
18749+
if (node) {
18750+
switch (node.kind) {
18751+
case SyntaxKind.IntersectionType:
18752+
case SyntaxKind.UnionType:
18753+
let commonEntityName: EntityName;
18754+
for (const typeNode of (<UnionOrIntersectionTypeNode>node).types) {
18755+
const individualEntityName = getEntityNameForDecoratorMetadata(typeNode);
18756+
if (!individualEntityName) {
18757+
// Individual is something like string number
18758+
// So it would be serialized to either that type or object
18759+
// Safe to return here
18760+
return undefined;
18761+
}
18762+
18763+
if (commonEntityName) {
18764+
// Note this is in sync with the transformation that happens for type node.
18765+
// Keep this in sync with serializeUnionOrIntersectionType
18766+
// Verify if they refer to same entity and is identifier
18767+
// return undefined if they dont match because we would emit object
18768+
if (!isIdentifier(commonEntityName) ||
18769+
!isIdentifier(individualEntityName) ||
18770+
commonEntityName.text !== individualEntityName.text) {
18771+
return undefined;
18772+
}
18773+
}
18774+
else {
18775+
commonEntityName = individualEntityName;
18776+
}
18777+
}
18778+
return commonEntityName;
18779+
18780+
case SyntaxKind.ParenthesizedType:
18781+
return getEntityNameForDecoratorMetadata((<ParenthesizedTypeNode>node).type);
18782+
18783+
case SyntaxKind.TypeReference:
18784+
return (<TypeReferenceNode>node).typeName;
18785+
}
18786+
}
18787+
}
18788+
1873118789
function getParameterTypeNodeForDecoratorCheck(node: ParameterDeclaration): TypeNode {
1873218790
return node.dotDotDotToken ? getRestParameterElementType(node.type) : node.type;
1873318791
}
@@ -18763,7 +18821,7 @@ namespace ts {
1876318821
const constructor = getFirstConstructorWithBody(<ClassDeclaration>node);
1876418822
if (constructor) {
1876518823
for (const parameter of constructor.parameters) {
18766-
markTypeNodeAsReferenced(getParameterTypeNodeForDecoratorCheck(parameter));
18824+
markDecoratorMedataDataTypeNodeAsReferenced(getParameterTypeNodeForDecoratorCheck(parameter));
1876718825
}
1876818826
}
1876918827
break;
@@ -18772,17 +18830,17 @@ namespace ts {
1877218830
case SyntaxKind.GetAccessor:
1877318831
case SyntaxKind.SetAccessor:
1877418832
for (const parameter of (<FunctionLikeDeclaration>node).parameters) {
18775-
markTypeNodeAsReferenced(getParameterTypeNodeForDecoratorCheck(parameter));
18833+
markDecoratorMedataDataTypeNodeAsReferenced(getParameterTypeNodeForDecoratorCheck(parameter));
1877618834
}
1877718835

18778-
markTypeNodeAsReferenced((<FunctionLikeDeclaration>node).type);
18836+
markDecoratorMedataDataTypeNodeAsReferenced((<FunctionLikeDeclaration>node).type);
1877918837
break;
1878018838

1878118839
case SyntaxKind.PropertyDeclaration:
18782-
markTypeNodeAsReferenced(getParameterTypeNodeForDecoratorCheck(<ParameterDeclaration>node));
18840+
markDecoratorMedataDataTypeNodeAsReferenced(getParameterTypeNodeForDecoratorCheck(<ParameterDeclaration>node));
1878318841
break;
1878418842
case SyntaxKind.Parameter:
18785-
markTypeNodeAsReferenced((<PropertyDeclaration>node).type);
18843+
markDecoratorMedataDataTypeNodeAsReferenced((<PropertyDeclaration>node).type);
1878618844
break;
1878718845
}
1878818846
}

src/compiler/transformers/ts.ts

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1762,23 +1762,19 @@ namespace ts {
17621762
}
17631763

17641764
function serializeUnionOrIntersectionType(node: UnionOrIntersectionTypeNode): SerializedTypeNode {
1765+
// Note when updating logic here also update getEntityNameForDecoratorMetadata
1766+
// so that aliases can be marked as referenced
17651767
let serializedUnion: SerializedTypeNode;
17661768
for (const typeNode of node.types) {
17671769
const serializedIndividual = serializeTypeNode(typeNode);
17681770

1769-
if (isVoidExpression(serializedIndividual)) {
1770-
// If we dont have any other type already set, set the initial type
1771-
if (!serializedUnion) {
1772-
serializedUnion = serializedIndividual;
1773-
}
1774-
}
1775-
else if (isIdentifier(serializedIndividual) && serializedIndividual.text === "Object") {
1771+
if (isIdentifier(serializedIndividual) && serializedIndividual.text === "Object") {
17761772
// One of the individual is global object, return immediately
17771773
return serializedIndividual;
17781774
}
17791775
// If there exists union that is not void 0 expression, check if the the common type is identifier.
17801776
// anything more complex and we will just default to Object
1781-
else if (serializedUnion && !isVoidExpression(serializedUnion)) {
1777+
else if (serializedUnion) {
17821778
// Different types
17831779
if (!isIdentifier(serializedUnion) ||
17841780
!isIdentifier(serializedIndividual) ||

src/compiler/utilities.ts

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3574,10 +3574,6 @@ namespace ts {
35743574
return node.kind === SyntaxKind.Identifier;
35753575
}
35763576

3577-
export function isVoidExpression(node: Node): node is VoidExpression {
3578-
return node.kind === SyntaxKind.VoidExpression;
3579-
}
3580-
35813577
export function isGeneratedIdentifier(node: Node): node is GeneratedIdentifier {
35823578
// Using `>` here catches both `GeneratedIdentifierKind.None` and `undefined`.
35833579
return isIdentifier(node) && node.autoGenerateKind > GeneratedIdentifierKind.None;
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
//// [tests/cases/compiler/metadataOfClassFromAlias.ts] ////
2+
3+
//// [auxiliry.ts]
4+
export class SomeClass {
5+
field: string;
6+
}
7+
8+
//// [test.ts]
9+
import { SomeClass } from './auxiliry';
10+
function annotation(): PropertyDecorator {
11+
return (target: any): void => { };
12+
}
13+
export class ClassA {
14+
@annotation() array: SomeClass | null;
15+
}
16+
17+
//// [auxiliry.js]
18+
"use strict";
19+
Object.defineProperty(exports, "__esModule", { value: true });
20+
var SomeClass = (function () {
21+
function SomeClass() {
22+
}
23+
return SomeClass;
24+
}());
25+
exports.SomeClass = SomeClass;
26+
//// [test.js]
27+
"use strict";
28+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
29+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
30+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
31+
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;
32+
return c > 3 && r && Object.defineProperty(target, key, r), r;
33+
};
34+
var __metadata = (this && this.__metadata) || function (k, v) {
35+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
36+
};
37+
Object.defineProperty(exports, "__esModule", { value: true });
38+
function annotation() {
39+
return function (target) { };
40+
}
41+
var ClassA = (function () {
42+
function ClassA() {
43+
}
44+
return ClassA;
45+
}());
46+
__decorate([
47+
annotation(),
48+
__metadata("design:type", Object)
49+
], ClassA.prototype, "array", void 0);
50+
exports.ClassA = ClassA;
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
=== tests/cases/compiler/auxiliry.ts ===
2+
export class SomeClass {
3+
>SomeClass : Symbol(SomeClass, Decl(auxiliry.ts, 0, 0))
4+
5+
field: string;
6+
>field : Symbol(SomeClass.field, Decl(auxiliry.ts, 0, 24))
7+
}
8+
9+
=== tests/cases/compiler/test.ts ===
10+
import { SomeClass } from './auxiliry';
11+
>SomeClass : Symbol(SomeClass, Decl(test.ts, 0, 8))
12+
13+
function annotation(): PropertyDecorator {
14+
>annotation : Symbol(annotation, Decl(test.ts, 0, 39))
15+
>PropertyDecorator : Symbol(PropertyDecorator, Decl(lib.d.ts, --, --))
16+
17+
return (target: any): void => { };
18+
>target : Symbol(target, Decl(test.ts, 2, 12))
19+
}
20+
export class ClassA {
21+
>ClassA : Symbol(ClassA, Decl(test.ts, 3, 1))
22+
23+
@annotation() array: SomeClass | null;
24+
>annotation : Symbol(annotation, Decl(test.ts, 0, 39))
25+
>array : Symbol(ClassA.array, Decl(test.ts, 4, 21))
26+
>SomeClass : Symbol(SomeClass, Decl(test.ts, 0, 8))
27+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
=== tests/cases/compiler/auxiliry.ts ===
2+
export class SomeClass {
3+
>SomeClass : SomeClass
4+
5+
field: string;
6+
>field : string
7+
}
8+
9+
=== tests/cases/compiler/test.ts ===
10+
import { SomeClass } from './auxiliry';
11+
>SomeClass : typeof SomeClass
12+
13+
function annotation(): PropertyDecorator {
14+
>annotation : () => PropertyDecorator
15+
>PropertyDecorator : PropertyDecorator
16+
17+
return (target: any): void => { };
18+
>(target: any): void => { } : (target: any) => void
19+
>target : any
20+
}
21+
export class ClassA {
22+
>ClassA : ClassA
23+
24+
@annotation() array: SomeClass | null;
25+
>annotation() : PropertyDecorator
26+
>annotation : () => PropertyDecorator
27+
>array : SomeClass
28+
>SomeClass : SomeClass
29+
>null : null
30+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
//// [tests/cases/compiler/metadataOfClassFromAlias2.ts] ////
2+
3+
//// [auxiliry.ts]
4+
export class SomeClass {
5+
field: string;
6+
}
7+
8+
//// [test.ts]
9+
import { SomeClass } from './auxiliry';
10+
function annotation(): PropertyDecorator {
11+
return (target: any): void => { };
12+
}
13+
export class ClassA {
14+
@annotation() array: SomeClass | null | string;
15+
}
16+
17+
//// [auxiliry.js]
18+
"use strict";
19+
Object.defineProperty(exports, "__esModule", { value: true });
20+
var SomeClass = (function () {
21+
function SomeClass() {
22+
}
23+
return SomeClass;
24+
}());
25+
exports.SomeClass = SomeClass;
26+
//// [test.js]
27+
"use strict";
28+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
29+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
30+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
31+
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;
32+
return c > 3 && r && Object.defineProperty(target, key, r), r;
33+
};
34+
var __metadata = (this && this.__metadata) || function (k, v) {
35+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
36+
};
37+
Object.defineProperty(exports, "__esModule", { value: true });
38+
function annotation() {
39+
return function (target) { };
40+
}
41+
var ClassA = (function () {
42+
function ClassA() {
43+
}
44+
return ClassA;
45+
}());
46+
__decorate([
47+
annotation(),
48+
__metadata("design:type", Object)
49+
], ClassA.prototype, "array", void 0);
50+
exports.ClassA = ClassA;
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
=== tests/cases/compiler/auxiliry.ts ===
2+
export class SomeClass {
3+
>SomeClass : Symbol(SomeClass, Decl(auxiliry.ts, 0, 0))
4+
5+
field: string;
6+
>field : Symbol(SomeClass.field, Decl(auxiliry.ts, 0, 24))
7+
}
8+
9+
=== tests/cases/compiler/test.ts ===
10+
import { SomeClass } from './auxiliry';
11+
>SomeClass : Symbol(SomeClass, Decl(test.ts, 0, 8))
12+
13+
function annotation(): PropertyDecorator {
14+
>annotation : Symbol(annotation, Decl(test.ts, 0, 39))
15+
>PropertyDecorator : Symbol(PropertyDecorator, Decl(lib.d.ts, --, --))
16+
17+
return (target: any): void => { };
18+
>target : Symbol(target, Decl(test.ts, 2, 12))
19+
}
20+
export class ClassA {
21+
>ClassA : Symbol(ClassA, Decl(test.ts, 3, 1))
22+
23+
@annotation() array: SomeClass | null | string;
24+
>annotation : Symbol(annotation, Decl(test.ts, 0, 39))
25+
>array : Symbol(ClassA.array, Decl(test.ts, 4, 21))
26+
>SomeClass : Symbol(SomeClass, Decl(test.ts, 0, 8))
27+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
=== tests/cases/compiler/auxiliry.ts ===
2+
export class SomeClass {
3+
>SomeClass : SomeClass
4+
5+
field: string;
6+
>field : string
7+
}
8+
9+
=== tests/cases/compiler/test.ts ===
10+
import { SomeClass } from './auxiliry';
11+
>SomeClass : typeof SomeClass
12+
13+
function annotation(): PropertyDecorator {
14+
>annotation : () => PropertyDecorator
15+
>PropertyDecorator : PropertyDecorator
16+
17+
return (target: any): void => { };
18+
>(target: any): void => { } : (target: any) => void
19+
>target : any
20+
}
21+
export class ClassA {
22+
>ClassA : ClassA
23+
24+
@annotation() array: SomeClass | null | string;
25+
>annotation() : PropertyDecorator
26+
>annotation : () => PropertyDecorator
27+
>array : string | SomeClass
28+
>SomeClass : SomeClass
29+
>null : null
30+
}

0 commit comments

Comments
 (0)