Skip to content

Commit eba15b5

Browse files
authored
Preserve literal types in contextual unions (#19966)
* Cherrypick non-comparability related changes from prolific literals PR * Renames and other style changes * Accept changes to new tests * Exclude the domain root from contextual typing literals except for type variables * Readd simple preservation fix * Add huge map test * Revert changes to widening on destructuring initalizers * Use tristate for subtype-reduction type * Rename type and argument * Move longer-running test to user suite
1 parent d01f4d1 commit eba15b5

14 files changed

+11007
-49
lines changed

src/compiler/checker.ts

Lines changed: 47 additions & 43 deletions
Large diffs are not rendered by default.

src/compiler/types.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2787,7 +2787,7 @@ namespace ts {
27872787
/* @internal */ getNullType(): Type;
27882788
/* @internal */ getESSymbolType(): Type;
27892789
/* @internal */ getNeverType(): Type;
2790-
/* @internal */ getUnionType(types: Type[], subtypeReduction?: boolean): Type;
2790+
/* @internal */ getUnionType(types: Type[], subtypeReduction?: UnionReduction): Type;
27912791
/* @internal */ createArrayType(elementType: Type): Type;
27922792
/* @internal */ createPromiseType(type: Type): Type;
27932793

@@ -2842,6 +2842,13 @@ namespace ts {
28422842
/* @internal */ getAccessibleSymbolChain(symbol: Symbol, enclosingDeclaration: Node | undefined, meaning: SymbolFlags, useOnlyExternalAliasing: boolean): Symbol[] | undefined;
28432843
}
28442844

2845+
/* @internal */
2846+
export const enum UnionReduction {
2847+
None = 0,
2848+
Literal,
2849+
Subtype
2850+
}
2851+
28452852
export enum NodeBuilderFlags {
28462853
None = 0,
28472854
// Options

src/services/codefixes/inferFromUsage.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -305,7 +305,7 @@ namespace ts.codefix {
305305
}
306306
}
307307
if (types.length) {
308-
const type = checker.getWidenedType(checker.getUnionType(types, /*subtypeReduction*/ true));
308+
const type = checker.getWidenedType(checker.getUnionType(types, UnionReduction.Subtype));
309309
paramTypes[parameterIndex] = isRestParameter ? checker.createArrayType(type) : type;
310310
}
311311
}
@@ -542,12 +542,12 @@ namespace ts.codefix {
542542
return checker.getStringType();
543543
}
544544
else if (usageContext.candidateTypes) {
545-
return checker.getWidenedType(checker.getUnionType(map(usageContext.candidateTypes, t => checker.getBaseTypeOfLiteralType(t)), /*subtypeReduction*/ true));
545+
return checker.getWidenedType(checker.getUnionType(map(usageContext.candidateTypes, t => checker.getBaseTypeOfLiteralType(t)), UnionReduction.Subtype));
546546
}
547547
else if (usageContext.properties && hasCallContext(usageContext.properties.get("then" as __String))) {
548548
const paramType = getParameterTypeFromCallContexts(0, usageContext.properties.get("then" as __String).callContexts, /*isRestParameter*/ false, checker);
549549
const types = paramType.getCallSignatures().map(c => c.getReturnType());
550-
return checker.createPromiseType(types.length ? checker.getUnionType(types, /*subtypeReduction*/ true) : checker.getAnyType());
550+
return checker.createPromiseType(types.length ? checker.getUnionType(types, UnionReduction.Subtype) : checker.getAnyType());
551551
}
552552
else if (usageContext.properties && hasCallContext(usageContext.properties.get("push" as __String))) {
553553
return checker.createArrayType(getParameterTypeFromCallContexts(0, usageContext.properties.get("push" as __String).callContexts, /*isRestParameter*/ false, checker));
@@ -610,7 +610,7 @@ namespace ts.codefix {
610610
}
611611

612612
if (types.length) {
613-
const type = checker.getWidenedType(checker.getUnionType(types, /*subtypeReduction*/ true));
613+
const type = checker.getWidenedType(checker.getUnionType(types, UnionReduction.Subtype));
614614
return isRestParameter ? checker.createArrayType(type) : type;
615615
}
616616
return undefined;
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
//// [contextualTypeShouldBeLiteral.ts]
2+
interface X {
3+
type: 'x';
4+
value: string;
5+
method(): void;
6+
}
7+
8+
interface Y {
9+
type: 'y';
10+
value: 'none' | 'done';
11+
method(): void;
12+
}
13+
14+
function foo(bar: X | Y) { }
15+
16+
foo({
17+
type: 'y',
18+
value: 'done',
19+
method() {
20+
this;
21+
this.type;
22+
this.value;
23+
}
24+
});
25+
26+
interface X2 {
27+
type1: 'x';
28+
value: string;
29+
method(): void;
30+
}
31+
32+
interface Y2 {
33+
type2: 'y';
34+
value: 'none' | 'done';
35+
method(): void;
36+
}
37+
38+
function foo2(bar: X2 | Y2) { }
39+
40+
foo2({
41+
type2: 'y',
42+
value: 'done',
43+
method() {
44+
this;
45+
this.value;
46+
}
47+
});
48+
49+
interface X3 {
50+
type: 'x';
51+
value: 1 | 2 | 3;
52+
xtra: number;
53+
}
54+
55+
interface Y3 {
56+
type: 'y';
57+
value: 11 | 12 | 13;
58+
ytra: number;
59+
}
60+
61+
let xy: X3 | Y3 = {
62+
type: 'y',
63+
value: 11,
64+
ytra: 12
65+
};
66+
67+
xy;
68+
69+
70+
interface LikeA {
71+
x: 'x';
72+
y: 'y';
73+
value: string;
74+
method(): void;
75+
}
76+
77+
interface LikeB {
78+
x: 'xx';
79+
y: 'yy';
80+
value: number;
81+
method(): void;
82+
}
83+
84+
let xyz: LikeA | LikeB = {
85+
x: 'x',
86+
y: 'y',
87+
value: "foo",
88+
method() {
89+
this;
90+
this.x;
91+
this.y;
92+
this.value;
93+
}
94+
};
95+
96+
xyz;
97+
98+
//// [contextualTypeShouldBeLiteral.js]
99+
"use strict";
100+
function foo(bar) { }
101+
foo({
102+
type: 'y',
103+
value: 'done',
104+
method: function () {
105+
this;
106+
this.type;
107+
this.value;
108+
}
109+
});
110+
function foo2(bar) { }
111+
foo2({
112+
type2: 'y',
113+
value: 'done',
114+
method: function () {
115+
this;
116+
this.value;
117+
}
118+
});
119+
var xy = {
120+
type: 'y',
121+
value: 11,
122+
ytra: 12
123+
};
124+
xy;
125+
var xyz = {
126+
x: 'x',
127+
y: 'y',
128+
value: "foo",
129+
method: function () {
130+
this;
131+
this.x;
132+
this.y;
133+
this.value;
134+
}
135+
};
136+
xyz;

0 commit comments

Comments
 (0)