Skip to content

Commit f52c4af

Browse files
authored
Merge pull request #24310 from Microsoft/nonWideningLiteralInferences
Non-widening literal type inferences
2 parents a9e89ce + b31c414 commit f52c4af

20 files changed

+443
-74
lines changed

src/compiler/checker.ts

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9286,8 +9286,10 @@ namespace ts {
92869286
return type;
92879287
}
92889288

9289-
function getRegularTypeOfLiteralType(type: Type) {
9290-
return type.flags & TypeFlags.StringOrNumberLiteral && type.flags & TypeFlags.FreshLiteral ? (<LiteralType>type).regularType : type;
9289+
function getRegularTypeOfLiteralType(type: Type): Type {
9290+
return type.flags & TypeFlags.StringOrNumberLiteral && type.flags & TypeFlags.FreshLiteral ? (<LiteralType>type).regularType :
9291+
type.flags & TypeFlags.Union ? getUnionType(sameMap((<UnionType>type).types, getRegularTypeOfLiteralType)) :
9292+
type;
92919293
}
92929294

92939295
function getLiteralType(value: string | number, enumId?: number, symbol?: Symbol) {
@@ -12774,10 +12776,12 @@ namespace ts {
1277412776
// all inferences were made to top-level occurrences of the type parameter, and
1277512777
// the type parameter has no constraint or its constraint includes no primitive or literal types, and
1277612778
// the type parameter was fixed during inference or does not occur at top-level in the return type.
12777-
const widenLiteralTypes = inference.topLevel &&
12778-
!hasPrimitiveConstraint(inference.typeParameter) &&
12779+
const primitiveConstraint = hasPrimitiveConstraint(inference.typeParameter);
12780+
const widenLiteralTypes = !primitiveConstraint && inference.topLevel &&
1277912781
(inference.isFixed || !isTypeParameterAtTopLevel(getReturnTypeOfSignature(signature), inference.typeParameter));
12780-
const baseCandidates = widenLiteralTypes ? sameMap(candidates, getWidenedLiteralType) : candidates;
12782+
const baseCandidates = primitiveConstraint ? sameMap(candidates, getRegularTypeOfLiteralType) :
12783+
widenLiteralTypes ? sameMap(candidates, getWidenedLiteralType) :
12784+
candidates;
1278112785
// If all inferences were made from contravariant positions, infer a common subtype. Otherwise, if
1278212786
// union types were requested or if all inferences were made from the return type position, infer a
1278312787
// union type. Otherwise, infer a common supertype.

tests/baselines/reference/bestCommonTypeOfConditionalExpressions2.types

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ var derived2: Derived2;
3030

3131
var r2 = true ? 1 : '';
3232
>r2 : string | number
33-
>true ? 1 : '' : 1 | ""
33+
>true ? 1 : '' : "" | 1
3434
>true : true
3535
>1 : 1
3636
>'' : ""

tests/baselines/reference/callExpressionWithTypeParameterConstrainedToOuterTypeParameter.types

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ var i: I<string>;
1515
>I : I<T>
1616

1717
var y = i(""); // y should be string
18-
>y : string
18+
>y : ""
1919
>i("") : ""
2020
>i : I<string>
2121
>"" : ""

tests/baselines/reference/conditionalExpression1.types

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
=== tests/cases/compiler/conditionalExpression1.ts ===
22
var x: boolean = (true ? 1 : ""); // should be an error
33
>x : boolean
4-
>(true ? 1 : "") : 1 | ""
5-
>true ? 1 : "" : 1 | ""
4+
>(true ? 1 : "") : "" | 1
5+
>true ? 1 : "" : "" | 1
66
>true : true
77
>1 : 1
88
>"" : ""

tests/baselines/reference/conditionalExpressions2.types

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ var b = false ? undefined : 0;
1515

1616
var c = false ? 1 : 0;
1717
>c : number
18-
>false ? 1 : 0 : 1 | 0
18+
>false ? 1 : 0 : 0 | 1
1919
>false : false
2020
>1 : 1
2121
>0 : 0

tests/baselines/reference/conditionalOperatorWithIdenticalBCT.types

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -218,7 +218,7 @@ var result10: (t: X) => any = true ? (m) => m.propertyX1 : (n) => n.propertyX2;
218218
//Expr1 and Expr2 are literals
219219
var result11: any = true ? 1 : 'string';
220220
>result11 : any
221-
>true ? 1 : 'string' : 1 | "string"
221+
>true ? 1 : 'string' : "string" | 1
222222
>true : true
223223
>1 : 1
224224
>'string' : "string"

tests/baselines/reference/conditionalTypes1.types

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -589,8 +589,8 @@ function zeroOf<T extends number | string | boolean>(value: T) {
589589
><ZeroOf<T>>(typeof value === "number" ? 0 : typeof value === "string" ? "" : false) : ZeroOf<T>
590590
>ZeroOf : ZeroOf<T>
591591
>T : T
592-
>(typeof value === "number" ? 0 : typeof value === "string" ? "" : false) : false | 0 | ""
593-
>typeof value === "number" ? 0 : typeof value === "string" ? "" : false : false | 0 | ""
592+
>(typeof value === "number" ? 0 : typeof value === "string" ? "" : false) : false | "" | 0
593+
>typeof value === "number" ? 0 : typeof value === "string" ? "" : false : false | "" | 0
594594
>typeof value === "number" : boolean
595595
>typeof value : "string" | "number" | "boolean" | "symbol" | "undefined" | "object" | "function"
596596
>value : T

tests/baselines/reference/declFileTypeAnnotationParenType.types

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ var y = [() => new c()];
2424
var k: (() => c) | string = (() => new c()) || "";
2525
>k : string | (() => c)
2626
>c : c
27-
>(() => new c()) || "" : (() => c) | ""
27+
>(() => new c()) || "" : "" | (() => c)
2828
>(() => new c()) : () => c
2929
>() => new c() : () => c
3030
>new c() : c

tests/baselines/reference/literalTypeWidening.js

Lines changed: 62 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,18 @@ function f5() {
6060
let v4 = c4;
6161
}
6262

63+
declare function widening<T>(x: T): T;
64+
declare function nonWidening<T extends string | number | symbol>(x: T): T;
65+
66+
function f6(cond: boolean) {
67+
let x1 = widening('a');
68+
let x2 = widening(10);
69+
let x3 = widening(cond ? 'a' : 10);
70+
let y1 = nonWidening('a');
71+
let y2 = nonWidening(10);
72+
let y3 = nonWidening(cond ? 'a' : 10);
73+
}
74+
6375
// Repro from #10898
6476

6577
type FAILURE = "FAILURE";
@@ -95,10 +107,33 @@ type TestEvent = "onmouseover" | "onmouseout";
95107

96108
function onMouseOver(): TestEvent { return "onmouseover"; }
97109

98-
let x = onMouseOver();
110+
let x = onMouseOver();
111+
112+
// Repro from #23649
113+
114+
export function Set<K extends string>(...keys: K[]): Record<K, true | undefined> {
115+
const result = {} as Record<K, true | undefined>
116+
keys.forEach(key => result[key] = true)
117+
return result
118+
}
119+
120+
export function keys<K extends string, V>(obj: Record<K, V>): K[] {
121+
return Object.keys(obj) as K[]
122+
}
123+
124+
type Obj = { code: LangCode }
125+
126+
const langCodeSet = Set('fr', 'en', 'es', 'it', 'nl')
127+
export type LangCode = keyof typeof langCodeSet
128+
export const langCodes = keys(langCodeSet)
129+
130+
const arr: Obj[] = langCodes.map(code => ({ code }))
131+
99132

100133
//// [literalTypeWidening.js]
134+
"use strict";
101135
// Widening vs. non-widening literal types
136+
exports.__esModule = true;
102137
function f1() {
103138
var c1 = "hello"; // Widening type "hello"
104139
var v1 = c1; // Type string
@@ -153,6 +188,14 @@ function f5() {
153188
var c4 = "foo";
154189
var v4 = c4;
155190
}
191+
function f6(cond) {
192+
var x1 = widening('a');
193+
var x2 = widening(10);
194+
var x3 = widening(cond ? 'a' : 10);
195+
var y1 = nonWidening('a');
196+
var y2 = nonWidening(10);
197+
var y3 = nonWidening(cond ? 'a' : 10);
198+
}
156199
var FAILURE = "FAILURE";
157200
function doWork() {
158201
return FAILURE;
@@ -172,3 +215,21 @@ if (isSuccess(result)) {
172215
}
173216
function onMouseOver() { return "onmouseover"; }
174217
var x = onMouseOver();
218+
// Repro from #23649
219+
function Set() {
220+
var keys = [];
221+
for (var _i = 0; _i < arguments.length; _i++) {
222+
keys[_i] = arguments[_i];
223+
}
224+
var result = {};
225+
keys.forEach(function (key) { return result[key] = true; });
226+
return result;
227+
}
228+
exports.Set = Set;
229+
function keys(obj) {
230+
return Object.keys(obj);
231+
}
232+
exports.keys = keys;
233+
var langCodeSet = Set('fr', 'en', 'es', 'it', 'nl');
234+
exports.langCodes = keys(langCodeSet);
235+
var arr = exports.langCodes.map(function (code) { return ({ code: code }); });

0 commit comments

Comments
 (0)