Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Mark as referenced aliases in Union that will get emitted as part of decorator metadata #13540

Merged
merged 4 commits into from
May 4, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 64 additions & 6 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18611,7 +18611,10 @@ namespace ts {
* marked as referenced to prevent import elision.
*/
function markTypeNodeAsReferenced(node: TypeNode) {
const typeName = node && getEntityNameFromTypeNode(node);
markEntityNameOrEntityExpressionAsReference(node && getEntityNameFromTypeNode(node));
}

function markEntityNameOrEntityExpressionAsReference(typeName: EntityNameOrEntityNameExpression) {
const rootName = typeName && getFirstIdentifier(typeName);
const rootSymbol = rootName && resolveName(rootName, rootName.text, (typeName.kind === SyntaxKind.Identifier ? SymbolFlags.Type : SymbolFlags.Namespace) | SymbolFlags.Alias, /*nameNotFoundMessage*/ undefined, /*nameArg*/ undefined);
if (rootSymbol
Expand All @@ -18622,6 +18625,61 @@ namespace ts {
}
}

/**
* This function marks the type used for metadata decorator as referenced if it is import
* from external module.
* This is different from markTypeNodeAsReferenced because it tries to simplify type nodes in
* union and intersection type
* @param node
*/
function markDecoratorMedataDataTypeNodeAsReferenced(node: TypeNode): void {
const entityName = getEntityNameForDecoratorMetadata(node);
if (entityName && isEntityName(entityName)) {
markEntityNameOrEntityExpressionAsReference(entityName);
}
}

function getEntityNameForDecoratorMetadata(node: TypeNode): EntityName {
if (node) {
switch (node.kind) {
case SyntaxKind.IntersectionType:
case SyntaxKind.UnionType:
let commonEntityName: EntityName;
for (const typeNode of (<UnionOrIntersectionTypeNode>node).types) {
const individualEntityName = getEntityNameForDecoratorMetadata(typeNode);
if (!individualEntityName) {
// Individual is something like string number
// So it would be serialized to either that type or object
// Safe to return here
return undefined;
}

if (commonEntityName) {
// Note this is in sync with the transformation that happens for type node.
// Keep this in sync with serializeUnionOrIntersectionType
// Verify if they refer to same entity and is identifier
// return undefined if they dont match because we would emit object
if (!isIdentifier(commonEntityName) ||
!isIdentifier(individualEntityName) ||
commonEntityName.text !== individualEntityName.text) {
return undefined;
}
}
else {
commonEntityName = individualEntityName;
}
}
return commonEntityName;

case SyntaxKind.ParenthesizedType:
return getEntityNameForDecoratorMetadata((<ParenthesizedTypeNode>node).type);

case SyntaxKind.TypeReference:
return (<TypeReferenceNode>node).typeName;
}
}
}

function getParameterTypeNodeForDecoratorCheck(node: ParameterDeclaration): TypeNode {
return node.dotDotDotToken ? getRestParameterElementType(node.type) : node.type;
}
Expand Down Expand Up @@ -18657,7 +18715,7 @@ namespace ts {
const constructor = getFirstConstructorWithBody(<ClassDeclaration>node);
if (constructor) {
for (const parameter of constructor.parameters) {
markTypeNodeAsReferenced(getParameterTypeNodeForDecoratorCheck(parameter));
markDecoratorMedataDataTypeNodeAsReferenced(getParameterTypeNodeForDecoratorCheck(parameter));
}
}
break;
Expand All @@ -18666,17 +18724,17 @@ namespace ts {
case SyntaxKind.GetAccessor:
case SyntaxKind.SetAccessor:
for (const parameter of (<FunctionLikeDeclaration>node).parameters) {
markTypeNodeAsReferenced(getParameterTypeNodeForDecoratorCheck(parameter));
markDecoratorMedataDataTypeNodeAsReferenced(getParameterTypeNodeForDecoratorCheck(parameter));
}

markTypeNodeAsReferenced((<FunctionLikeDeclaration>node).type);
markDecoratorMedataDataTypeNodeAsReferenced((<FunctionLikeDeclaration>node).type);
break;

case SyntaxKind.PropertyDeclaration:
markTypeNodeAsReferenced(getParameterTypeNodeForDecoratorCheck(<ParameterDeclaration>node));
markDecoratorMedataDataTypeNodeAsReferenced(getParameterTypeNodeForDecoratorCheck(<ParameterDeclaration>node));
break;
case SyntaxKind.Parameter:
markTypeNodeAsReferenced((<PropertyDeclaration>node).type);
markDecoratorMedataDataTypeNodeAsReferenced((<PropertyDeclaration>node).type);
break;
}
}
Expand Down
12 changes: 4 additions & 8 deletions src/compiler/transformers/ts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1761,23 +1761,19 @@ namespace ts {
}

function serializeUnionOrIntersectionType(node: UnionOrIntersectionTypeNode): SerializedTypeNode {
// 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) {
const serializedIndividual = serializeTypeNode(typeNode);

if (isVoidExpression(serializedIndividual)) {
// If we dont have any other type already set, set the initial type
if (!serializedUnion) {
serializedUnion = serializedIndividual;
}
}
else if (isIdentifier(serializedIndividual) && serializedIndividual.text === "Object") {
if (isIdentifier(serializedIndividual) && serializedIndividual.text === "Object") {
// One of the individual is global object, return immediately
return serializedIndividual;
}
// If there exists union that is not void 0 expression, check if the the common type is identifier.
// anything more complex and we will just default to Object
else if (serializedUnion && !isVoidExpression(serializedUnion)) {
else if (serializedUnion) {
// Different types
if (!isIdentifier(serializedUnion) ||
!isIdentifier(serializedIndividual) ||
Expand Down
4 changes: 0 additions & 4 deletions src/compiler/utilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3566,10 +3566,6 @@ 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;
Expand Down
50 changes: 50 additions & 0 deletions tests/baselines/reference/metadataOfClassFromAlias.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
//// [tests/cases/compiler/metadataOfClassFromAlias.ts] ////

//// [auxiliry.ts]
export class SomeClass {
field: string;
}

//// [test.ts]
import { SomeClass } from './auxiliry';
function annotation(): PropertyDecorator {
return (target: any): void => { };
}
export class ClassA {
@annotation() array: SomeClass | null;
}

//// [auxiliry.js]
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var SomeClass = (function () {
function SomeClass() {
}
return SomeClass;
}());
exports.SomeClass = SomeClass;
//// [test.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);
};
Object.defineProperty(exports, "__esModule", { value: true });
function annotation() {
return function (target) { };
}
var ClassA = (function () {
function ClassA() {
}
return ClassA;
}());
__decorate([
annotation(),
__metadata("design:type", Object)
], ClassA.prototype, "array", void 0);
exports.ClassA = ClassA;
27 changes: 27 additions & 0 deletions tests/baselines/reference/metadataOfClassFromAlias.symbols
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
=== tests/cases/compiler/auxiliry.ts ===
export class SomeClass {
>SomeClass : Symbol(SomeClass, Decl(auxiliry.ts, 0, 0))

field: string;
>field : Symbol(SomeClass.field, Decl(auxiliry.ts, 0, 24))
}

=== tests/cases/compiler/test.ts ===
import { SomeClass } from './auxiliry';
>SomeClass : Symbol(SomeClass, Decl(test.ts, 0, 8))

function annotation(): PropertyDecorator {
>annotation : Symbol(annotation, Decl(test.ts, 0, 39))
>PropertyDecorator : Symbol(PropertyDecorator, Decl(lib.d.ts, --, --))

return (target: any): void => { };
>target : Symbol(target, Decl(test.ts, 2, 12))
}
export class ClassA {
>ClassA : Symbol(ClassA, Decl(test.ts, 3, 1))

@annotation() array: SomeClass | null;
>annotation : Symbol(annotation, Decl(test.ts, 0, 39))
>array : Symbol(ClassA.array, Decl(test.ts, 4, 21))
>SomeClass : Symbol(SomeClass, Decl(test.ts, 0, 8))
}
30 changes: 30 additions & 0 deletions tests/baselines/reference/metadataOfClassFromAlias.types
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
=== tests/cases/compiler/auxiliry.ts ===
export class SomeClass {
>SomeClass : SomeClass

field: string;
>field : string
}

=== tests/cases/compiler/test.ts ===
import { SomeClass } from './auxiliry';
>SomeClass : typeof SomeClass

function annotation(): PropertyDecorator {
>annotation : () => PropertyDecorator
>PropertyDecorator : PropertyDecorator

return (target: any): void => { };
>(target: any): void => { } : (target: any) => void
>target : any
}
export class ClassA {
>ClassA : ClassA

@annotation() array: SomeClass | null;
>annotation() : PropertyDecorator
>annotation : () => PropertyDecorator
>array : SomeClass
>SomeClass : SomeClass
>null : null
}
50 changes: 50 additions & 0 deletions tests/baselines/reference/metadataOfClassFromAlias2.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
//// [tests/cases/compiler/metadataOfClassFromAlias2.ts] ////

//// [auxiliry.ts]
export class SomeClass {
field: string;
}

//// [test.ts]
import { SomeClass } from './auxiliry';
function annotation(): PropertyDecorator {
return (target: any): void => { };
}
export class ClassA {
@annotation() array: SomeClass | null | string;
}

//// [auxiliry.js]
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var SomeClass = (function () {
function SomeClass() {
}
return SomeClass;
}());
exports.SomeClass = SomeClass;
//// [test.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);
};
Object.defineProperty(exports, "__esModule", { value: true });
function annotation() {
return function (target) { };
}
var ClassA = (function () {
function ClassA() {
}
return ClassA;
}());
__decorate([
annotation(),
__metadata("design:type", Object)
], ClassA.prototype, "array", void 0);
exports.ClassA = ClassA;
27 changes: 27 additions & 0 deletions tests/baselines/reference/metadataOfClassFromAlias2.symbols
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
=== tests/cases/compiler/auxiliry.ts ===
export class SomeClass {
>SomeClass : Symbol(SomeClass, Decl(auxiliry.ts, 0, 0))

field: string;
>field : Symbol(SomeClass.field, Decl(auxiliry.ts, 0, 24))
}

=== tests/cases/compiler/test.ts ===
import { SomeClass } from './auxiliry';
>SomeClass : Symbol(SomeClass, Decl(test.ts, 0, 8))

function annotation(): PropertyDecorator {
>annotation : Symbol(annotation, Decl(test.ts, 0, 39))
>PropertyDecorator : Symbol(PropertyDecorator, Decl(lib.d.ts, --, --))

return (target: any): void => { };
>target : Symbol(target, Decl(test.ts, 2, 12))
}
export class ClassA {
>ClassA : Symbol(ClassA, Decl(test.ts, 3, 1))

@annotation() array: SomeClass | null | string;
>annotation : Symbol(annotation, Decl(test.ts, 0, 39))
>array : Symbol(ClassA.array, Decl(test.ts, 4, 21))
>SomeClass : Symbol(SomeClass, Decl(test.ts, 0, 8))
}
30 changes: 30 additions & 0 deletions tests/baselines/reference/metadataOfClassFromAlias2.types
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
=== tests/cases/compiler/auxiliry.ts ===
export class SomeClass {
>SomeClass : SomeClass

field: string;
>field : string
}

=== tests/cases/compiler/test.ts ===
import { SomeClass } from './auxiliry';
>SomeClass : typeof SomeClass

function annotation(): PropertyDecorator {
>annotation : () => PropertyDecorator
>PropertyDecorator : PropertyDecorator

return (target: any): void => { };
>(target: any): void => { } : (target: any) => void
>target : any
}
export class ClassA {
>ClassA : ClassA

@annotation() array: SomeClass | null | string;
>annotation() : PropertyDecorator
>annotation : () => PropertyDecorator
>array : string | SomeClass
>SomeClass : SomeClass
>null : null
}
Loading