Skip to content

Commit 73a8292

Browse files
committed
Support union of non identifier serialized type node with null/undefined/never
1 parent 5940379 commit 73a8292

File tree

5 files changed

+168
-47
lines changed

5 files changed

+168
-47
lines changed

src/compiler/transformers/ts.ts

Lines changed: 46 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1637,6 +1637,8 @@ namespace ts {
16371637
return createVoidZero();
16381638
}
16391639

1640+
type SerializedTypeNode = SerializedEntityNameAsExpression | VoidExpression | ConditionalExpression;
1641+
16401642
/**
16411643
* Serializes a type node for use with decorator type metadata.
16421644
*
@@ -1655,7 +1657,7 @@ namespace ts {
16551657
*
16561658
* @param node The type node to serialize.
16571659
*/
1658-
function serializeTypeNode(node: TypeNode): Expression {
1660+
function serializeTypeNode(node: TypeNode): SerializedTypeNode {
16591661
if (node === undefined) {
16601662
return createIdentifier("Object");
16611663
}
@@ -1664,6 +1666,7 @@ namespace ts {
16641666
case SyntaxKind.VoidKeyword:
16651667
case SyntaxKind.UndefinedKeyword:
16661668
case SyntaxKind.NullKeyword:
1669+
case SyntaxKind.NeverKeyword:
16671670
return createVoidZero();
16681671

16691672
case SyntaxKind.ParenthesizedType:
@@ -1715,53 +1718,15 @@ namespace ts {
17151718

17161719
case SyntaxKind.IntersectionType:
17171720
case SyntaxKind.UnionType:
1718-
{
1719-
const unionOrIntersection = <UnionOrIntersectionTypeNode>node;
1720-
let serializedUnion: Identifier | VoidExpression;
1721-
for (const typeNode of unionOrIntersection.types) {
1722-
const serializedIndividual = serializeTypeNode(typeNode);
1723-
1724-
if (isIdentifier(serializedIndividual)) {
1725-
// One of the individual is global object, return immediately
1726-
if (serializedIndividual.text === "Object") {
1727-
return serializedIndividual;
1728-
}
1729-
1730-
// Different types
1731-
if (serializedUnion && isIdentifier(serializedUnion) && serializedUnion.text !== serializedIndividual.text) {
1732-
serializedUnion = undefined;
1733-
break;
1734-
}
1735-
1736-
serializedUnion = serializedIndividual;
1737-
}
1738-
else if (isVoidExpression(serializedIndividual)) {
1739-
// If we dont have any other type already set, set the initial type
1740-
if (!serializedUnion) {
1741-
serializedUnion = serializedIndividual;
1742-
}
1743-
}
1744-
else {
1745-
// Non identifier and undefined/null
1746-
serializedUnion = undefined;
1747-
break;
1748-
}
1749-
}
1721+
return serializeUnionOrIntersectionType(<UnionOrIntersectionTypeNode>node);
17501722

1751-
// If we were able to find common type
1752-
if (serializedUnion) {
1753-
return serializedUnion;
1754-
}
1755-
}
1756-
// Fallthrough
17571723
case SyntaxKind.TypeQuery:
17581724
case SyntaxKind.TypeOperator:
17591725
case SyntaxKind.IndexedAccessType:
17601726
case SyntaxKind.MappedType:
17611727
case SyntaxKind.TypeLiteral:
17621728
case SyntaxKind.AnyKeyword:
17631729
case SyntaxKind.ThisType:
1764-
case SyntaxKind.NeverKeyword:
17651730
break;
17661731

17671732
default:
@@ -1772,13 +1737,48 @@ namespace ts {
17721737
return createIdentifier("Object");
17731738
}
17741739

1740+
function serializeUnionOrIntersectionType(node: UnionOrIntersectionTypeNode): SerializedTypeNode {
1741+
let serializedUnion: SerializedTypeNode;
1742+
for (const typeNode of node.types) {
1743+
const serializedIndividual = serializeTypeNode(typeNode);
1744+
1745+
if (isVoidExpression(serializedIndividual)) {
1746+
// If we dont have any other type already set, set the initial type
1747+
if (!serializedUnion) {
1748+
serializedUnion = serializedIndividual;
1749+
}
1750+
}
1751+
else if (isIdentifier(serializedIndividual) && serializedIndividual.text === "Object") {
1752+
// One of the individual is global object, return immediately
1753+
return serializedIndividual;
1754+
}
1755+
// If there exists union that is not void 0 expression, check if the the common type is identifier.
1756+
// anything more complex and we will just default to Object
1757+
else if (serializedUnion && !isVoidExpression(serializedUnion)) {
1758+
// Different types
1759+
if (!isIdentifier(serializedUnion) ||
1760+
!isIdentifier(serializedIndividual) ||
1761+
serializedUnion.text !== serializedIndividual.text) {
1762+
return createIdentifier("Object");
1763+
}
1764+
}
1765+
else {
1766+
// Initialize the union type
1767+
serializedUnion = serializedIndividual;
1768+
}
1769+
}
1770+
1771+
// If we were able to find common type, use it
1772+
return serializedUnion;
1773+
}
1774+
17751775
/**
17761776
* Serializes a TypeReferenceNode to an appropriate JS constructor value for use with
17771777
* decorator type metadata.
17781778
*
17791779
* @param node The type reference node.
17801780
*/
1781-
function serializeTypeReferenceNode(node: TypeReferenceNode) {
1781+
function serializeTypeReferenceNode(node: TypeReferenceNode): SerializedTypeNode {
17821782
switch (resolver.getTypeReferenceSerializationKind(node.typeName, currentScope)) {
17831783
case TypeReferenceSerializationKind.Unknown:
17841784
const serialized = serializeEntityNameAsExpression(node.typeName, /*useFallback*/ true);
@@ -1826,14 +1826,15 @@ namespace ts {
18261826
}
18271827
}
18281828

1829+
type SerializedEntityNameAsExpression = Identifier | BinaryExpression | PropertyAccessExpression;
18291830
/**
18301831
* Serializes an entity name as an expression for decorator type metadata.
18311832
*
18321833
* @param node The entity name to serialize.
18331834
* @param useFallback A value indicating whether to use logical operators to test for the
18341835
* entity name at runtime.
18351836
*/
1836-
function serializeEntityNameAsExpression(node: EntityName, useFallback: boolean): Expression {
1837+
function serializeEntityNameAsExpression(node: EntityName, useFallback: boolean): SerializedEntityNameAsExpression {
18371838
switch (node.kind) {
18381839
case SyntaxKind.Identifier:
18391840
// Create a clone of the name with a new parent, and treat it as if it were
@@ -1866,8 +1867,8 @@ namespace ts {
18661867
* @param useFallback A value indicating whether to use logical operators to test for the
18671868
* qualified name at runtime.
18681869
*/
1869-
function serializeQualifiedNameAsExpression(node: QualifiedName, useFallback: boolean): Expression {
1870-
let left: Expression;
1870+
function serializeQualifiedNameAsExpression(node: QualifiedName, useFallback: boolean): PropertyAccessExpression {
1871+
let left: SerializedEntityNameAsExpression;
18711872
if (node.left.kind === SyntaxKind.Identifier) {
18721873
left = serializeEntityNameAsExpression(node.left, useFallback);
18731874
}
@@ -1892,7 +1893,7 @@ namespace ts {
18921893
* Gets an expression that points to the global "Symbol" constructor at runtime if it is
18931894
* available.
18941895
*/
1895-
function getGlobalSymbolNameWithFallback(): Expression {
1896+
function getGlobalSymbolNameWithFallback(): ConditionalExpression {
18961897
return createConditional(
18971898
createTypeCheck(createIdentifier("Symbol"), "function"),
18981899
createIdentifier("Symbol"),

tests/baselines/reference/metadataOfUnionWithNull.js

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,21 @@ class B {
2525

2626
@PropDeco
2727
d: undefined | null;
28+
29+
@PropDeco
30+
e: symbol | null;
31+
32+
@PropDeco
33+
f: symbol | A;
34+
35+
@PropDeco
36+
g: A | null;
37+
38+
@PropDeco
39+
h: null | B;
40+
41+
@PropDeco
42+
j: null | symbol;
2843
}
2944

3045
//// [metadataOfUnionWithNull.js]
@@ -54,7 +69,7 @@ __decorate([
5469
], B.prototype, "x");
5570
__decorate([
5671
PropDeco,
57-
__metadata("design:type", Object)
72+
__metadata("design:type", Boolean)
5873
], B.prototype, "y");
5974
__decorate([
6075
PropDeco,
@@ -66,7 +81,7 @@ __decorate([
6681
], B.prototype, "a");
6782
__decorate([
6883
PropDeco,
69-
__metadata("design:type", Object)
84+
__metadata("design:type", void 0)
7085
], B.prototype, "b");
7186
__decorate([
7287
PropDeco,
@@ -76,3 +91,23 @@ __decorate([
7691
PropDeco,
7792
__metadata("design:type", void 0)
7893
], B.prototype, "d");
94+
__decorate([
95+
PropDeco,
96+
__metadata("design:type", typeof Symbol === "function" ? Symbol : Object)
97+
], B.prototype, "e");
98+
__decorate([
99+
PropDeco,
100+
__metadata("design:type", Object)
101+
], B.prototype, "f");
102+
__decorate([
103+
PropDeco,
104+
__metadata("design:type", A)
105+
], B.prototype, "g");
106+
__decorate([
107+
PropDeco,
108+
__metadata("design:type", B)
109+
], B.prototype, "h");
110+
__decorate([
111+
PropDeco,
112+
__metadata("design:type", typeof Symbol === "function" ? Symbol : Object)
113+
], B.prototype, "j");

tests/baselines/reference/metadataOfUnionWithNull.symbols

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,4 +53,37 @@ class B {
5353

5454
d: undefined | null;
5555
>d : Symbol(B.d, Decl(metadataOfUnionWithNull.ts, 22, 17))
56+
57+
@PropDeco
58+
>PropDeco : Symbol(PropDeco, Decl(metadataOfUnionWithNull.ts, 0, 0))
59+
60+
e: symbol | null;
61+
>e : Symbol(B.e, Decl(metadataOfUnionWithNull.ts, 25, 24))
62+
63+
@PropDeco
64+
>PropDeco : Symbol(PropDeco, Decl(metadataOfUnionWithNull.ts, 0, 0))
65+
66+
f: symbol | A;
67+
>f : Symbol(B.f, Decl(metadataOfUnionWithNull.ts, 28, 21))
68+
>A : Symbol(A, Decl(metadataOfUnionWithNull.ts, 0, 63))
69+
70+
@PropDeco
71+
>PropDeco : Symbol(PropDeco, Decl(metadataOfUnionWithNull.ts, 0, 0))
72+
73+
g: A | null;
74+
>g : Symbol(B.g, Decl(metadataOfUnionWithNull.ts, 31, 18))
75+
>A : Symbol(A, Decl(metadataOfUnionWithNull.ts, 0, 63))
76+
77+
@PropDeco
78+
>PropDeco : Symbol(PropDeco, Decl(metadataOfUnionWithNull.ts, 0, 0))
79+
80+
h: null | B;
81+
>h : Symbol(B.h, Decl(metadataOfUnionWithNull.ts, 34, 16))
82+
>B : Symbol(B, Decl(metadataOfUnionWithNull.ts, 3, 1))
83+
84+
@PropDeco
85+
>PropDeco : Symbol(PropDeco, Decl(metadataOfUnionWithNull.ts, 0, 0))
86+
87+
j: null | symbol;
88+
>j : Symbol(B.j, Decl(metadataOfUnionWithNull.ts, 37, 16))
5689
}

tests/baselines/reference/metadataOfUnionWithNull.types

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,5 +56,42 @@ class B {
5656

5757
d: undefined | null;
5858
>d : null
59+
>null : null
60+
61+
@PropDeco
62+
>PropDeco : (target: Object, propKey: string | symbol) => void
63+
64+
e: symbol | null;
65+
>e : symbol
66+
>null : null
67+
68+
@PropDeco
69+
>PropDeco : (target: Object, propKey: string | symbol) => void
70+
71+
f: symbol | A;
72+
>f : symbol | A
73+
>A : A
74+
75+
@PropDeco
76+
>PropDeco : (target: Object, propKey: string | symbol) => void
77+
78+
g: A | null;
79+
>g : A
80+
>A : A
81+
>null : null
82+
83+
@PropDeco
84+
>PropDeco : (target: Object, propKey: string | symbol) => void
85+
86+
h: null | B;
87+
>h : B
88+
>null : null
89+
>B : B
90+
91+
@PropDeco
92+
>PropDeco : (target: Object, propKey: string | symbol) => void
93+
94+
j: null | symbol;
95+
>j : symbol
5996
>null : null
6097
}

tests/cases/compiler/metadataOfUnionWithNull.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,4 +26,19 @@ class B {
2626

2727
@PropDeco
2828
d: undefined | null;
29+
30+
@PropDeco
31+
e: symbol | null;
32+
33+
@PropDeco
34+
f: symbol | A;
35+
36+
@PropDeco
37+
g: A | null;
38+
39+
@PropDeco
40+
h: null | B;
41+
42+
@PropDeco
43+
j: null | symbol;
2944
}

0 commit comments

Comments
 (0)