Skip to content
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
75 changes: 41 additions & 34 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24172,41 +24172,10 @@ namespace ts {
switch (node.kind) {
case SyntaxKind.IntersectionType:
case SyntaxKind.UnionType:
let commonEntityName: EntityName | undefined;
for (let typeNode of (<UnionOrIntersectionTypeNode>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 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;
}
return getEntityNameForDecoratorMetadataFromTypeList((<UnionOrIntersectionTypeNode>node).types);

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.escapedText !== individualEntityName.escapedText) {
return undefined;
}
}
else {
commonEntityName = individualEntityName;
}
}
return commonEntityName;
case SyntaxKind.ConditionalType:
return getEntityNameForDecoratorMetadataFromTypeList([(<ConditionalTypeNode>node).trueType, (<ConditionalTypeNode>node).falseType]);

case SyntaxKind.ParenthesizedType:
return getEntityNameForDecoratorMetadata((<ParenthesizedTypeNode>node).type);
Expand All @@ -24217,6 +24186,44 @@ namespace ts {
}
}

function getEntityNameForDecoratorMetadataFromTypeList(types: ReadonlyArray<TypeNode>): EntityName | undefined {
let commonEntityName: EntityName | undefined;
for (let typeNode of 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 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.escapedText !== individualEntityName.escapedText) {
return undefined;
}
}
else {
commonEntityName = individualEntityName;
}
}
return commonEntityName;
}

function getParameterTypeNodeForDecoratorCheck(node: ParameterDeclaration): TypeNode | undefined {
const typeNode = getEffectiveTypeAnnotationNode(node);
return isRestParameter(node) ? getRestParameterElementType(typeNode) : typeNode;
Expand Down
15 changes: 12 additions & 3 deletions src/compiler/transformers/ts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1928,7 +1928,10 @@ namespace ts {

case SyntaxKind.IntersectionType:
case SyntaxKind.UnionType:
return serializeUnionOrIntersectionType(<UnionOrIntersectionTypeNode>node);
return serializeTypeList((<UnionOrIntersectionTypeNode>node).types);

case SyntaxKind.ConditionalType:
return serializeTypeList([(<ConditionalTypeNode>node).trueType, (<ConditionalTypeNode>node).falseType]);

case SyntaxKind.TypeQuery:
case SyntaxKind.TypeOperator:
Expand All @@ -1941,18 +1944,19 @@ namespace ts {
case SyntaxKind.ImportType:
break;


default:
return Debug.failBadSyntaxKind(node);
}

return createIdentifier("Object");
}

function serializeUnionOrIntersectionType(node: UnionOrIntersectionTypeNode): SerializedTypeNode {
function serializeTypeList(types: ReadonlyArray<TypeNode>): SerializedTypeNode {
// Note when updating logic here also update getEntityNameForDecoratorMetadata
// so that aliases can be marked as referenced
let serializedUnion: SerializedTypeNode | undefined;
for (let typeNode of node.types) {
for (let typeNode of types) {
while (typeNode.kind === SyntaxKind.ParenthesizedType) {
typeNode = (typeNode as ParenthesizedTypeNode).type; // Skip parens if need be
}
Expand Down Expand Up @@ -1998,6 +2002,11 @@ namespace ts {
const kind = resolver.getTypeReferenceSerializationKind(node.typeName, currentNameScope || currentLexicalScope);
switch (kind) {
case TypeReferenceSerializationKind.Unknown:
// From conditional type type reference that cannot be resolved is Similar to any or unknown
if (findAncestor(node, n => n.parent && isConditionalTypeNode(n.parent) && (n.parent.trueType === n || n.parent.falseType === n))) {
return createIdentifier("Object");
}

const serialized = serializeEntityNameAsExpressionFallback(node.typeName);
const temp = createTempVariable(hoistVariableDeclaration);
return createConditional(
Expand Down
39 changes: 39 additions & 0 deletions tests/baselines/reference/decoratorMetadataConditionalType.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
//// [decoratorMetadataConditionalType.ts]
declare function d(): PropertyDecorator;
abstract class BaseEntity<T> {
@d()
public attributes: T extends { attributes: infer A } ? A : undefined;
}
class C {
@d()
x: number extends string ? false : true;
}

//// [decoratorMetadataConditionalType.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 BaseEntity = /** @class */ (function () {
function BaseEntity() {
}
__decorate([
d(),
__metadata("design:type", Object)
], BaseEntity.prototype, "attributes");
return BaseEntity;
}());
var C = /** @class */ (function () {
function C() {
}
__decorate([
d(),
__metadata("design:type", Boolean)
], C.prototype, "x");
return C;
}());
28 changes: 28 additions & 0 deletions tests/baselines/reference/decoratorMetadataConditionalType.symbols
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
=== tests/cases/compiler/decoratorMetadataConditionalType.ts ===
declare function d(): PropertyDecorator;
>d : Symbol(d, Decl(decoratorMetadataConditionalType.ts, 0, 0))
>PropertyDecorator : Symbol(PropertyDecorator, Decl(lib.es5.d.ts, --, --))

abstract class BaseEntity<T> {
>BaseEntity : Symbol(BaseEntity, Decl(decoratorMetadataConditionalType.ts, 0, 40))
>T : Symbol(T, Decl(decoratorMetadataConditionalType.ts, 1, 26))

@d()
>d : Symbol(d, Decl(decoratorMetadataConditionalType.ts, 0, 0))

public attributes: T extends { attributes: infer A } ? A : undefined;
>attributes : Symbol(BaseEntity.attributes, Decl(decoratorMetadataConditionalType.ts, 1, 30))
>T : Symbol(T, Decl(decoratorMetadataConditionalType.ts, 1, 26))
>attributes : Symbol(attributes, Decl(decoratorMetadataConditionalType.ts, 3, 34))
>A : Symbol(A, Decl(decoratorMetadataConditionalType.ts, 3, 52))
>A : Symbol(A, Decl(decoratorMetadataConditionalType.ts, 3, 52))
}
class C {
>C : Symbol(C, Decl(decoratorMetadataConditionalType.ts, 4, 1))

@d()
>d : Symbol(d, Decl(decoratorMetadataConditionalType.ts, 0, 0))

x: number extends string ? false : true;
>x : Symbol(C.x, Decl(decoratorMetadataConditionalType.ts, 5, 9))
}
27 changes: 27 additions & 0 deletions tests/baselines/reference/decoratorMetadataConditionalType.types
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
=== tests/cases/compiler/decoratorMetadataConditionalType.ts ===
declare function d(): PropertyDecorator;
>d : () => PropertyDecorator

abstract class BaseEntity<T> {
>BaseEntity : BaseEntity<T>

@d()
>d() : PropertyDecorator
>d : () => PropertyDecorator

public attributes: T extends { attributes: infer A } ? A : undefined;
>attributes : T extends { attributes: infer A; } ? A : undefined
>attributes : A
}
class C {
>C : C

@d()
>d() : PropertyDecorator
>d : () => PropertyDecorator

x: number extends string ? false : true;
>x : true
>false : false
>true : true
}
12 changes: 12 additions & 0 deletions tests/cases/compiler/decoratorMetadataConditionalType.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// @experimentalDecorators: true
// @emitDecoratorMetadata: true

declare function d(): PropertyDecorator;
abstract class BaseEntity<T> {
@d()
public attributes: T extends { attributes: infer A } ? A : undefined;
}
class C {
@d()
x: number extends string ? false : true;
}