Skip to content

Commit 6def72b

Browse files
authored
Merge pull request #31950 from microsoft/unionObjectAndArrayLiterals
Union inferences from object and array literals
2 parents fc773a8 + f38a4aa commit 6def72b

12 files changed

+420
-32
lines changed

src/compiler/checker.ts

Lines changed: 31 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5481,7 +5481,7 @@ namespace ts {
54815481
function getTypeFromObjectBindingPattern(pattern: ObjectBindingPattern, includePatternInType: boolean, reportErrors: boolean): Type {
54825482
const members = createSymbolTable();
54835483
let stringIndexInfo: IndexInfo | undefined;
5484-
let objectFlags = ObjectFlags.ObjectLiteral | ObjectFlags.ContainsObjectLiteral;
5484+
let objectFlags = ObjectFlags.ObjectLiteral | ObjectFlags.ContainsObjectOrArrayLiteral;
54855485
forEach(pattern.elements, e => {
54865486
const name = e.propertyName || <Identifier>e.name;
54875487
if (e.dotDotDotToken) {
@@ -10803,7 +10803,7 @@ namespace ts {
1080310803
emptyArray,
1080410804
getIndexInfoWithReadonly(stringIndexInfo, readonly),
1080510805
getIndexInfoWithReadonly(numberIndexInfo, readonly));
10806-
spread.objectFlags |= ObjectFlags.ObjectLiteral | ObjectFlags.ContainsObjectLiteral | ObjectFlags.ContainsSpread | objectFlags;
10806+
spread.objectFlags |= ObjectFlags.ObjectLiteral | ObjectFlags.ContainsObjectOrArrayLiteral | ObjectFlags.ContainsSpread | objectFlags;
1080710807
return spread;
1080810808
}
1080910809

@@ -15644,12 +15644,16 @@ namespace ts {
1564415644
return !!(getObjectFlags(type) & ObjectFlags.ObjectLiteral);
1564515645
}
1564615646

15647-
function widenObjectLiteralCandidates(candidates: Type[]): Type[] {
15647+
function isObjectOrArrayLiteralType(type: Type) {
15648+
return !!(getObjectFlags(type) & (ObjectFlags.ObjectLiteral | ObjectFlags.ArrayLiteral));
15649+
}
15650+
15651+
function unionObjectAndArrayLiteralCandidates(candidates: Type[]): Type[] {
1564815652
if (candidates.length > 1) {
15649-
const objectLiterals = filter(candidates, isObjectLiteralType);
15653+
const objectLiterals = filter(candidates, isObjectOrArrayLiteralType);
1565015654
if (objectLiterals.length) {
15651-
const objectLiteralsType = getWidenedType(getUnionType(objectLiterals, UnionReduction.Subtype));
15652-
return concatenate(filter(candidates, t => !isObjectLiteralType(t)), [objectLiteralsType]);
15655+
const literalsType = getUnionType(objectLiterals, UnionReduction.Subtype);
15656+
return concatenate(filter(candidates, t => !isObjectOrArrayLiteralType(t)), [literalsType]);
1565315657
}
1565415658
}
1565515659
return candidates;
@@ -15660,8 +15664,8 @@ namespace ts {
1566015664
}
1566115665

1566215666
function getCovariantInference(inference: InferenceInfo, signature: Signature) {
15663-
// Extract all object literal types and replace them with a single widened and normalized type.
15664-
const candidates = widenObjectLiteralCandidates(inference.candidates!);
15667+
// Extract all object and array literal types and replace them with a single widened and normalized type.
15668+
const candidates = unionObjectAndArrayLiteralCandidates(inference.candidates!);
1566515669
// We widen inferred literal types if
1566615670
// all inferences were made to top-level occurrences of the type parameter, and
1566715671
// the type parameter has no constraint or its constraint includes no primitive or literal types, and
@@ -19147,22 +19151,34 @@ namespace ts {
1914719151
const minLength = elementCount - (hasRestElement ? 1 : 0);
1914819152
// If array literal is actually a destructuring pattern, mark it as an implied type. We do this such
1914919153
// that we get the same behavior for "var [x, y] = []" and "[x, y] = []".
19150-
let tupleResult: Type | undefined;
19154+
let tupleResult;
1915119155
if (inDestructuringPattern && minLength > 0) {
1915219156
const type = cloneTypeReference(<TypeReference>createTupleType(elementTypes, minLength, hasRestElement));
1915319157
type.pattern = node;
1915419158
return type;
1915519159
}
1915619160
else if (tupleResult = getArrayLiteralTupleTypeIfApplicable(elementTypes, contextualType, hasRestElement, elementCount, inConstContext)) {
19157-
return tupleResult;
19161+
return createArrayLiteralType(tupleResult);
1915819162
}
1915919163
else if (forceTuple) {
19160-
return createTupleType(elementTypes, minLength, hasRestElement);
19164+
return createArrayLiteralType(createTupleType(elementTypes, minLength, hasRestElement));
1916119165
}
1916219166
}
19163-
return createArrayType(elementTypes.length ?
19167+
return createArrayLiteralType(createArrayType(elementTypes.length ?
1916419168
getUnionType(elementTypes, UnionReduction.Subtype) :
19165-
strictNullChecks ? implicitNeverType : undefinedWideningType, inConstContext);
19169+
strictNullChecks ? implicitNeverType : undefinedWideningType, inConstContext));
19170+
}
19171+
19172+
function createArrayLiteralType(type: ObjectType) {
19173+
if (!(getObjectFlags(type) & ObjectFlags.Reference)) {
19174+
return type;
19175+
}
19176+
let literalType = (<TypeReference>type).literalType;
19177+
if (!literalType) {
19178+
literalType = (<TypeReference>type).literalType = cloneTypeReference(<TypeReference>type);
19179+
literalType.objectFlags |= ObjectFlags.ArrayLiteral | ObjectFlags.ContainsObjectOrArrayLiteral;
19180+
}
19181+
return literalType;
1916619182
}
1916719183

1916819184
function getArrayLiteralTupleTypeIfApplicable(elementTypes: Type[], contextualType: Type | undefined, hasRestElement: boolean, elementCount = elementTypes.length, readonly = false) {
@@ -19447,7 +19463,7 @@ namespace ts {
1944719463
const stringIndexInfo = hasComputedStringProperty ? getObjectLiteralIndexInfo(node, offset, propertiesArray, IndexKind.String) : undefined;
1944819464
const numberIndexInfo = hasComputedNumberProperty ? getObjectLiteralIndexInfo(node, offset, propertiesArray, IndexKind.Number) : undefined;
1944919465
const result = createAnonymousType(node.symbol, propertiesTable, emptyArray, emptyArray, stringIndexInfo, numberIndexInfo);
19450-
result.objectFlags |= objectFlags | ObjectFlags.ObjectLiteral | ObjectFlags.ContainsObjectLiteral;
19466+
result.objectFlags |= objectFlags | ObjectFlags.ObjectLiteral | ObjectFlags.ContainsObjectOrArrayLiteral;
1945119467
if (isJSObjectLiteral) {
1945219468
result.objectFlags |= ObjectFlags.JSLiteral;
1945319469
}
@@ -19643,7 +19659,7 @@ namespace ts {
1964319659
function createJsxAttributesType() {
1964419660
objectFlags |= freshObjectLiteralFlag;
1964519661
const result = createAnonymousType(attributes.symbol, attributesTable, emptyArray, emptyArray, /*stringIndexInfo*/ undefined, /*numberIndexInfo*/ undefined);
19646-
result.objectFlags |= objectFlags | ObjectFlags.ObjectLiteral | ObjectFlags.ContainsObjectLiteral;
19662+
result.objectFlags |= objectFlags | ObjectFlags.ObjectLiteral | ObjectFlags.ContainsObjectOrArrayLiteral;
1964719663
return result;
1964819664
}
1964919665
}

src/compiler/types.ts

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4089,19 +4089,20 @@ namespace ts {
40894089
MarkerType = 1 << 13, // Marker type used for variance probing
40904090
JSLiteral = 1 << 14, // Object type declared in JS - disables errors on read/write of nonexisting members
40914091
FreshLiteral = 1 << 15, // Fresh object literal
4092+
ArrayLiteral = 1 << 16, // Originates in an array literal
40924093
/* @internal */
4093-
PrimitiveUnion = 1 << 16, // Union of only primitive types
4094+
PrimitiveUnion = 1 << 17, // Union of only primitive types
40944095
/* @internal */
4095-
ContainsWideningType = 1 << 17, // Type is or contains undefined or null widening type
4096+
ContainsWideningType = 1 << 18, // Type is or contains undefined or null widening type
40964097
/* @internal */
4097-
ContainsObjectLiteral = 1 << 18, // Type is or contains object literal type
4098+
ContainsObjectOrArrayLiteral = 1 << 19, // Type is or contains object literal type
40984099
/* @internal */
4099-
NonInferrableType = 1 << 19, // Type is or contains anyFunctionType or silentNeverType
4100+
NonInferrableType = 1 << 20, // Type is or contains anyFunctionType or silentNeverType
41004101
ClassOrInterface = Class | Interface,
41014102
/* @internal */
4102-
RequiresWidening = ContainsWideningType | ContainsObjectLiteral,
4103+
RequiresWidening = ContainsWideningType | ContainsObjectOrArrayLiteral,
41034104
/* @internal */
4104-
PropagatingFlags = ContainsWideningType | ContainsObjectLiteral | NonInferrableType
4105+
PropagatingFlags = ContainsWideningType | ContainsObjectOrArrayLiteral | NonInferrableType
41054106
}
41064107

41074108
/* @internal */
@@ -4154,6 +4155,8 @@ namespace ts {
41544155
export interface TypeReference extends ObjectType {
41554156
target: GenericType; // Type reference target
41564157
typeArguments?: ReadonlyArray<Type>; // Type reference type arguments (undefined if none)
4158+
/* @internal */
4159+
literalType?: TypeReference; // Clone of type with ObjectFlags.ArrayLiteral set
41574160
}
41584161

41594162
/* @internal */

tests/baselines/reference/api/tsserverlibrary.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2303,6 +2303,7 @@ declare namespace ts {
23032303
MarkerType = 8192,
23042304
JSLiteral = 16384,
23052305
FreshLiteral = 32768,
2306+
ArrayLiteral = 65536,
23062307
ClassOrInterface = 3,
23072308
}
23082309
interface ObjectType extends Type {

tests/baselines/reference/api/typescript.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2303,6 +2303,7 @@ declare namespace ts {
23032303
MarkerType = 8192,
23042304
JSLiteral = 16384,
23052305
FreshLiteral = 32768,
2306+
ArrayLiteral = 65536,
23062307
ClassOrInterface = 3,
23072308
}
23082309
interface ObjectType extends Type {
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
//// [arrayLiteralInference.ts]
2+
// Repro from #31204
3+
4+
export enum AppType {
5+
HeaderDetail = 'HeaderDetail',
6+
HeaderMultiDetail = 'HeaderMultiDetail',
7+
AdvancedList = 'AdvancedList',
8+
Standard = 'Standard',
9+
Relationship = 'Relationship',
10+
Report = 'Report',
11+
Composite = 'Composite',
12+
ListOnly = 'ListOnly',
13+
ModuleSettings = 'ModuleSettings'
14+
}
15+
16+
export enum AppStyle {
17+
Tree,
18+
TreeEntity,
19+
Standard,
20+
MiniApp,
21+
PivotTable
22+
}
23+
24+
const appTypeStylesWithError: Map<AppType, Array<AppStyle>> = new Map([
25+
[AppType.Standard, [AppStyle.Standard, AppStyle.MiniApp]],
26+
[AppType.Relationship, [AppStyle.Standard, AppStyle.Tree, AppStyle.TreeEntity]],
27+
[AppType.AdvancedList, [AppStyle.Standard, AppStyle.MiniApp]]
28+
]);
29+
30+
// Repro from #31204
31+
32+
declare function foo<T>(...args: T[]): T[];
33+
let b1: { x: boolean }[] = foo({ x: true }, { x: false });
34+
let b2: boolean[][] = foo([true], [false]);
35+
36+
37+
//// [arrayLiteralInference.js]
38+
// Repro from #31204
39+
export var AppType;
40+
(function (AppType) {
41+
AppType["HeaderDetail"] = "HeaderDetail";
42+
AppType["HeaderMultiDetail"] = "HeaderMultiDetail";
43+
AppType["AdvancedList"] = "AdvancedList";
44+
AppType["Standard"] = "Standard";
45+
AppType["Relationship"] = "Relationship";
46+
AppType["Report"] = "Report";
47+
AppType["Composite"] = "Composite";
48+
AppType["ListOnly"] = "ListOnly";
49+
AppType["ModuleSettings"] = "ModuleSettings";
50+
})(AppType || (AppType = {}));
51+
export var AppStyle;
52+
(function (AppStyle) {
53+
AppStyle[AppStyle["Tree"] = 0] = "Tree";
54+
AppStyle[AppStyle["TreeEntity"] = 1] = "TreeEntity";
55+
AppStyle[AppStyle["Standard"] = 2] = "Standard";
56+
AppStyle[AppStyle["MiniApp"] = 3] = "MiniApp";
57+
AppStyle[AppStyle["PivotTable"] = 4] = "PivotTable";
58+
})(AppStyle || (AppStyle = {}));
59+
const appTypeStylesWithError = new Map([
60+
[AppType.Standard, [AppStyle.Standard, AppStyle.MiniApp]],
61+
[AppType.Relationship, [AppStyle.Standard, AppStyle.Tree, AppStyle.TreeEntity]],
62+
[AppType.AdvancedList, [AppStyle.Standard, AppStyle.MiniApp]]
63+
]);
64+
let b1 = foo({ x: true }, { x: false });
65+
let b2 = foo([true], [false]);
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
=== tests/cases/conformance/expressions/arrayLiterals/arrayLiteralInference.ts ===
2+
// Repro from #31204
3+
4+
export enum AppType {
5+
>AppType : Symbol(AppType, Decl(arrayLiteralInference.ts, 0, 0))
6+
7+
HeaderDetail = 'HeaderDetail',
8+
>HeaderDetail : Symbol(AppType.HeaderDetail, Decl(arrayLiteralInference.ts, 2, 21))
9+
10+
HeaderMultiDetail = 'HeaderMultiDetail',
11+
>HeaderMultiDetail : Symbol(AppType.HeaderMultiDetail, Decl(arrayLiteralInference.ts, 3, 34))
12+
13+
AdvancedList = 'AdvancedList',
14+
>AdvancedList : Symbol(AppType.AdvancedList, Decl(arrayLiteralInference.ts, 4, 44))
15+
16+
Standard = 'Standard',
17+
>Standard : Symbol(AppType.Standard, Decl(arrayLiteralInference.ts, 5, 34))
18+
19+
Relationship = 'Relationship',
20+
>Relationship : Symbol(AppType.Relationship, Decl(arrayLiteralInference.ts, 6, 26))
21+
22+
Report = 'Report',
23+
>Report : Symbol(AppType.Report, Decl(arrayLiteralInference.ts, 7, 34))
24+
25+
Composite = 'Composite',
26+
>Composite : Symbol(AppType.Composite, Decl(arrayLiteralInference.ts, 8, 22))
27+
28+
ListOnly = 'ListOnly',
29+
>ListOnly : Symbol(AppType.ListOnly, Decl(arrayLiteralInference.ts, 9, 28))
30+
31+
ModuleSettings = 'ModuleSettings'
32+
>ModuleSettings : Symbol(AppType.ModuleSettings, Decl(arrayLiteralInference.ts, 10, 26))
33+
}
34+
35+
export enum AppStyle {
36+
>AppStyle : Symbol(AppStyle, Decl(arrayLiteralInference.ts, 12, 1))
37+
38+
Tree,
39+
>Tree : Symbol(AppStyle.Tree, Decl(arrayLiteralInference.ts, 14, 22))
40+
41+
TreeEntity,
42+
>TreeEntity : Symbol(AppStyle.TreeEntity, Decl(arrayLiteralInference.ts, 15, 9))
43+
44+
Standard,
45+
>Standard : Symbol(AppStyle.Standard, Decl(arrayLiteralInference.ts, 16, 15))
46+
47+
MiniApp,
48+
>MiniApp : Symbol(AppStyle.MiniApp, Decl(arrayLiteralInference.ts, 17, 13))
49+
50+
PivotTable
51+
>PivotTable : Symbol(AppStyle.PivotTable, Decl(arrayLiteralInference.ts, 18, 12))
52+
}
53+
54+
const appTypeStylesWithError: Map<AppType, Array<AppStyle>> = new Map([
55+
>appTypeStylesWithError : Symbol(appTypeStylesWithError, Decl(arrayLiteralInference.ts, 22, 5))
56+
>Map : Symbol(Map, Decl(lib.es2015.collection.d.ts, --, --), Decl(lib.es2015.collection.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --))
57+
>AppType : Symbol(AppType, Decl(arrayLiteralInference.ts, 0, 0))
58+
>Array : Symbol(Array, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --))
59+
>AppStyle : Symbol(AppStyle, Decl(arrayLiteralInference.ts, 12, 1))
60+
>Map : Symbol(Map, Decl(lib.es2015.collection.d.ts, --, --), Decl(lib.es2015.collection.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --))
61+
62+
[AppType.Standard, [AppStyle.Standard, AppStyle.MiniApp]],
63+
>AppType.Standard : Symbol(AppType.Standard, Decl(arrayLiteralInference.ts, 5, 34))
64+
>AppType : Symbol(AppType, Decl(arrayLiteralInference.ts, 0, 0))
65+
>Standard : Symbol(AppType.Standard, Decl(arrayLiteralInference.ts, 5, 34))
66+
>AppStyle.Standard : Symbol(AppStyle.Standard, Decl(arrayLiteralInference.ts, 16, 15))
67+
>AppStyle : Symbol(AppStyle, Decl(arrayLiteralInference.ts, 12, 1))
68+
>Standard : Symbol(AppStyle.Standard, Decl(arrayLiteralInference.ts, 16, 15))
69+
>AppStyle.MiniApp : Symbol(AppStyle.MiniApp, Decl(arrayLiteralInference.ts, 17, 13))
70+
>AppStyle : Symbol(AppStyle, Decl(arrayLiteralInference.ts, 12, 1))
71+
>MiniApp : Symbol(AppStyle.MiniApp, Decl(arrayLiteralInference.ts, 17, 13))
72+
73+
[AppType.Relationship, [AppStyle.Standard, AppStyle.Tree, AppStyle.TreeEntity]],
74+
>AppType.Relationship : Symbol(AppType.Relationship, Decl(arrayLiteralInference.ts, 6, 26))
75+
>AppType : Symbol(AppType, Decl(arrayLiteralInference.ts, 0, 0))
76+
>Relationship : Symbol(AppType.Relationship, Decl(arrayLiteralInference.ts, 6, 26))
77+
>AppStyle.Standard : Symbol(AppStyle.Standard, Decl(arrayLiteralInference.ts, 16, 15))
78+
>AppStyle : Symbol(AppStyle, Decl(arrayLiteralInference.ts, 12, 1))
79+
>Standard : Symbol(AppStyle.Standard, Decl(arrayLiteralInference.ts, 16, 15))
80+
>AppStyle.Tree : Symbol(AppStyle.Tree, Decl(arrayLiteralInference.ts, 14, 22))
81+
>AppStyle : Symbol(AppStyle, Decl(arrayLiteralInference.ts, 12, 1))
82+
>Tree : Symbol(AppStyle.Tree, Decl(arrayLiteralInference.ts, 14, 22))
83+
>AppStyle.TreeEntity : Symbol(AppStyle.TreeEntity, Decl(arrayLiteralInference.ts, 15, 9))
84+
>AppStyle : Symbol(AppStyle, Decl(arrayLiteralInference.ts, 12, 1))
85+
>TreeEntity : Symbol(AppStyle.TreeEntity, Decl(arrayLiteralInference.ts, 15, 9))
86+
87+
[AppType.AdvancedList, [AppStyle.Standard, AppStyle.MiniApp]]
88+
>AppType.AdvancedList : Symbol(AppType.AdvancedList, Decl(arrayLiteralInference.ts, 4, 44))
89+
>AppType : Symbol(AppType, Decl(arrayLiteralInference.ts, 0, 0))
90+
>AdvancedList : Symbol(AppType.AdvancedList, Decl(arrayLiteralInference.ts, 4, 44))
91+
>AppStyle.Standard : Symbol(AppStyle.Standard, Decl(arrayLiteralInference.ts, 16, 15))
92+
>AppStyle : Symbol(AppStyle, Decl(arrayLiteralInference.ts, 12, 1))
93+
>Standard : Symbol(AppStyle.Standard, Decl(arrayLiteralInference.ts, 16, 15))
94+
>AppStyle.MiniApp : Symbol(AppStyle.MiniApp, Decl(arrayLiteralInference.ts, 17, 13))
95+
>AppStyle : Symbol(AppStyle, Decl(arrayLiteralInference.ts, 12, 1))
96+
>MiniApp : Symbol(AppStyle.MiniApp, Decl(arrayLiteralInference.ts, 17, 13))
97+
98+
]);
99+
100+
// Repro from #31204
101+
102+
declare function foo<T>(...args: T[]): T[];
103+
>foo : Symbol(foo, Decl(arrayLiteralInference.ts, 26, 3))
104+
>T : Symbol(T, Decl(arrayLiteralInference.ts, 30, 21))
105+
>args : Symbol(args, Decl(arrayLiteralInference.ts, 30, 24))
106+
>T : Symbol(T, Decl(arrayLiteralInference.ts, 30, 21))
107+
>T : Symbol(T, Decl(arrayLiteralInference.ts, 30, 21))
108+
109+
let b1: { x: boolean }[] = foo({ x: true }, { x: false });
110+
>b1 : Symbol(b1, Decl(arrayLiteralInference.ts, 31, 3))
111+
>x : Symbol(x, Decl(arrayLiteralInference.ts, 31, 9))
112+
>foo : Symbol(foo, Decl(arrayLiteralInference.ts, 26, 3))
113+
>x : Symbol(x, Decl(arrayLiteralInference.ts, 31, 32))
114+
>x : Symbol(x, Decl(arrayLiteralInference.ts, 31, 45))
115+
116+
let b2: boolean[][] = foo([true], [false]);
117+
>b2 : Symbol(b2, Decl(arrayLiteralInference.ts, 32, 3))
118+
>foo : Symbol(foo, Decl(arrayLiteralInference.ts, 26, 3))
119+

0 commit comments

Comments
 (0)