Skip to content

Commit b8968fa

Browse files
authored
Merge pull request #28485 from Microsoft/conditionalTypeDecorator
When serializing conditional types, use true and false type to determine emit
2 parents 0077016 + cce26c8 commit b8968fa

File tree

6 files changed

+159
-37
lines changed

6 files changed

+159
-37
lines changed

src/compiler/checker.ts

Lines changed: 41 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -24172,41 +24172,10 @@ namespace ts {
2417224172
switch (node.kind) {
2417324173
case SyntaxKind.IntersectionType:
2417424174
case SyntaxKind.UnionType:
24175-
let commonEntityName: EntityName | undefined;
24176-
for (let typeNode of (<UnionOrIntersectionTypeNode>node).types) {
24177-
while (typeNode.kind === SyntaxKind.ParenthesizedType) {
24178-
typeNode = (typeNode as ParenthesizedTypeNode).type; // Skip parens if need be
24179-
}
24180-
if (typeNode.kind === SyntaxKind.NeverKeyword) {
24181-
continue; // Always elide `never` from the union/intersection if possible
24182-
}
24183-
if (!strictNullChecks && (typeNode.kind === SyntaxKind.NullKeyword || typeNode.kind === SyntaxKind.UndefinedKeyword)) {
24184-
continue; // Elide null and undefined from unions for metadata, just like what we did prior to the implementation of strict null checks
24185-
}
24186-
const individualEntityName = getEntityNameForDecoratorMetadata(typeNode);
24187-
if (!individualEntityName) {
24188-
// Individual is something like string number
24189-
// So it would be serialized to either that type or object
24190-
// Safe to return here
24191-
return undefined;
24192-
}
24175+
return getEntityNameForDecoratorMetadataFromTypeList((<UnionOrIntersectionTypeNode>node).types);
2419324176

24194-
if (commonEntityName) {
24195-
// Note this is in sync with the transformation that happens for type node.
24196-
// Keep this in sync with serializeUnionOrIntersectionType
24197-
// Verify if they refer to same entity and is identifier
24198-
// return undefined if they dont match because we would emit object
24199-
if (!isIdentifier(commonEntityName) ||
24200-
!isIdentifier(individualEntityName) ||
24201-
commonEntityName.escapedText !== individualEntityName.escapedText) {
24202-
return undefined;
24203-
}
24204-
}
24205-
else {
24206-
commonEntityName = individualEntityName;
24207-
}
24208-
}
24209-
return commonEntityName;
24177+
case SyntaxKind.ConditionalType:
24178+
return getEntityNameForDecoratorMetadataFromTypeList([(<ConditionalTypeNode>node).trueType, (<ConditionalTypeNode>node).falseType]);
2421024179

2421124180
case SyntaxKind.ParenthesizedType:
2421224181
return getEntityNameForDecoratorMetadata((<ParenthesizedTypeNode>node).type);
@@ -24217,6 +24186,44 @@ namespace ts {
2421724186
}
2421824187
}
2421924188

24189+
function getEntityNameForDecoratorMetadataFromTypeList(types: ReadonlyArray<TypeNode>): EntityName | undefined {
24190+
let commonEntityName: EntityName | undefined;
24191+
for (let typeNode of types) {
24192+
while (typeNode.kind === SyntaxKind.ParenthesizedType) {
24193+
typeNode = (typeNode as ParenthesizedTypeNode).type; // Skip parens if need be
24194+
}
24195+
if (typeNode.kind === SyntaxKind.NeverKeyword) {
24196+
continue; // Always elide `never` from the union/intersection if possible
24197+
}
24198+
if (!strictNullChecks && (typeNode.kind === SyntaxKind.NullKeyword || typeNode.kind === SyntaxKind.UndefinedKeyword)) {
24199+
continue; // Elide null and undefined from unions for metadata, just like what we did prior to the implementation of strict null checks
24200+
}
24201+
const individualEntityName = getEntityNameForDecoratorMetadata(typeNode);
24202+
if (!individualEntityName) {
24203+
// Individual is something like string number
24204+
// So it would be serialized to either that type or object
24205+
// Safe to return here
24206+
return undefined;
24207+
}
24208+
24209+
if (commonEntityName) {
24210+
// Note this is in sync with the transformation that happens for type node.
24211+
// Keep this in sync with serializeUnionOrIntersectionType
24212+
// Verify if they refer to same entity and is identifier
24213+
// return undefined if they dont match because we would emit object
24214+
if (!isIdentifier(commonEntityName) ||
24215+
!isIdentifier(individualEntityName) ||
24216+
commonEntityName.escapedText !== individualEntityName.escapedText) {
24217+
return undefined;
24218+
}
24219+
}
24220+
else {
24221+
commonEntityName = individualEntityName;
24222+
}
24223+
}
24224+
return commonEntityName;
24225+
}
24226+
2422024227
function getParameterTypeNodeForDecoratorCheck(node: ParameterDeclaration): TypeNode | undefined {
2422124228
const typeNode = getEffectiveTypeAnnotationNode(node);
2422224229
return isRestParameter(node) ? getRestParameterElementType(typeNode) : typeNode;

src/compiler/transformers/ts.ts

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1928,7 +1928,10 @@ namespace ts {
19281928

19291929
case SyntaxKind.IntersectionType:
19301930
case SyntaxKind.UnionType:
1931-
return serializeUnionOrIntersectionType(<UnionOrIntersectionTypeNode>node);
1931+
return serializeTypeList((<UnionOrIntersectionTypeNode>node).types);
1932+
1933+
case SyntaxKind.ConditionalType:
1934+
return serializeTypeList([(<ConditionalTypeNode>node).trueType, (<ConditionalTypeNode>node).falseType]);
19321935

19331936
case SyntaxKind.TypeQuery:
19341937
case SyntaxKind.TypeOperator:
@@ -1941,18 +1944,19 @@ namespace ts {
19411944
case SyntaxKind.ImportType:
19421945
break;
19431946

1947+
19441948
default:
19451949
return Debug.failBadSyntaxKind(node);
19461950
}
19471951

19481952
return createIdentifier("Object");
19491953
}
19501954

1951-
function serializeUnionOrIntersectionType(node: UnionOrIntersectionTypeNode): SerializedTypeNode {
1955+
function serializeTypeList(types: ReadonlyArray<TypeNode>): SerializedTypeNode {
19521956
// Note when updating logic here also update getEntityNameForDecoratorMetadata
19531957
// so that aliases can be marked as referenced
19541958
let serializedUnion: SerializedTypeNode | undefined;
1955-
for (let typeNode of node.types) {
1959+
for (let typeNode of types) {
19561960
while (typeNode.kind === SyntaxKind.ParenthesizedType) {
19571961
typeNode = (typeNode as ParenthesizedTypeNode).type; // Skip parens if need be
19581962
}
@@ -1998,6 +2002,11 @@ namespace ts {
19982002
const kind = resolver.getTypeReferenceSerializationKind(node.typeName, currentNameScope || currentLexicalScope);
19992003
switch (kind) {
20002004
case TypeReferenceSerializationKind.Unknown:
2005+
// From conditional type type reference that cannot be resolved is Similar to any or unknown
2006+
if (findAncestor(node, n => n.parent && isConditionalTypeNode(n.parent) && (n.parent.trueType === n || n.parent.falseType === n))) {
2007+
return createIdentifier("Object");
2008+
}
2009+
20012010
const serialized = serializeEntityNameAsExpressionFallback(node.typeName);
20022011
const temp = createTempVariable(hoistVariableDeclaration);
20032012
return createConditional(
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
//// [decoratorMetadataConditionalType.ts]
2+
declare function d(): PropertyDecorator;
3+
abstract class BaseEntity<T> {
4+
@d()
5+
public attributes: T extends { attributes: infer A } ? A : undefined;
6+
}
7+
class C {
8+
@d()
9+
x: number extends string ? false : true;
10+
}
11+
12+
//// [decoratorMetadataConditionalType.js]
13+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
14+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
15+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
16+
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;
17+
return c > 3 && r && Object.defineProperty(target, key, r), r;
18+
};
19+
var __metadata = (this && this.__metadata) || function (k, v) {
20+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
21+
};
22+
var BaseEntity = /** @class */ (function () {
23+
function BaseEntity() {
24+
}
25+
__decorate([
26+
d(),
27+
__metadata("design:type", Object)
28+
], BaseEntity.prototype, "attributes");
29+
return BaseEntity;
30+
}());
31+
var C = /** @class */ (function () {
32+
function C() {
33+
}
34+
__decorate([
35+
d(),
36+
__metadata("design:type", Boolean)
37+
], C.prototype, "x");
38+
return C;
39+
}());
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
=== tests/cases/compiler/decoratorMetadataConditionalType.ts ===
2+
declare function d(): PropertyDecorator;
3+
>d : Symbol(d, Decl(decoratorMetadataConditionalType.ts, 0, 0))
4+
>PropertyDecorator : Symbol(PropertyDecorator, Decl(lib.es5.d.ts, --, --))
5+
6+
abstract class BaseEntity<T> {
7+
>BaseEntity : Symbol(BaseEntity, Decl(decoratorMetadataConditionalType.ts, 0, 40))
8+
>T : Symbol(T, Decl(decoratorMetadataConditionalType.ts, 1, 26))
9+
10+
@d()
11+
>d : Symbol(d, Decl(decoratorMetadataConditionalType.ts, 0, 0))
12+
13+
public attributes: T extends { attributes: infer A } ? A : undefined;
14+
>attributes : Symbol(BaseEntity.attributes, Decl(decoratorMetadataConditionalType.ts, 1, 30))
15+
>T : Symbol(T, Decl(decoratorMetadataConditionalType.ts, 1, 26))
16+
>attributes : Symbol(attributes, Decl(decoratorMetadataConditionalType.ts, 3, 34))
17+
>A : Symbol(A, Decl(decoratorMetadataConditionalType.ts, 3, 52))
18+
>A : Symbol(A, Decl(decoratorMetadataConditionalType.ts, 3, 52))
19+
}
20+
class C {
21+
>C : Symbol(C, Decl(decoratorMetadataConditionalType.ts, 4, 1))
22+
23+
@d()
24+
>d : Symbol(d, Decl(decoratorMetadataConditionalType.ts, 0, 0))
25+
26+
x: number extends string ? false : true;
27+
>x : Symbol(C.x, Decl(decoratorMetadataConditionalType.ts, 5, 9))
28+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
=== tests/cases/compiler/decoratorMetadataConditionalType.ts ===
2+
declare function d(): PropertyDecorator;
3+
>d : () => PropertyDecorator
4+
5+
abstract class BaseEntity<T> {
6+
>BaseEntity : BaseEntity<T>
7+
8+
@d()
9+
>d() : PropertyDecorator
10+
>d : () => PropertyDecorator
11+
12+
public attributes: T extends { attributes: infer A } ? A : undefined;
13+
>attributes : T extends { attributes: infer A; } ? A : undefined
14+
>attributes : A
15+
}
16+
class C {
17+
>C : C
18+
19+
@d()
20+
>d() : PropertyDecorator
21+
>d : () => PropertyDecorator
22+
23+
x: number extends string ? false : true;
24+
>x : true
25+
>false : false
26+
>true : true
27+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// @experimentalDecorators: true
2+
// @emitDecoratorMetadata: true
3+
4+
declare function d(): PropertyDecorator;
5+
abstract class BaseEntity<T> {
6+
@d()
7+
public attributes: T extends { attributes: infer A } ? A : undefined;
8+
}
9+
class C {
10+
@d()
11+
x: number extends string ? false : true;
12+
}

0 commit comments

Comments
 (0)