-
Notifications
You must be signed in to change notification settings - Fork 12.8k
Spread & rest over intersection and unions #12552
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
f2e30f6
5c10764
5c4f145
216f286
86b48e3
9ab55c1
e4f0a50
17b6645
51f5ef6
2178d55
2152683
505c153
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3054,7 +3054,15 @@ namespace ts { | |
} | ||
|
||
function getRestType(source: Type, properties: PropertyName[], symbol: Symbol): Type { | ||
Debug.assert(!!(source.flags & TypeFlags.Object), "Rest types only support object types right now."); | ||
source = filterType(source, t => !(t.flags & TypeFlags.Nullable)); | ||
if (source.flags & TypeFlags.Never) { | ||
return emptyObjectType; | ||
} | ||
|
||
if (source.flags & TypeFlags.Union) { | ||
return mapType(source, t => getRestType(t, properties, symbol)); | ||
} | ||
|
||
const members = createMap<Symbol>(); | ||
const names = createMap<true>(); | ||
for (const name of properties) { | ||
|
@@ -3095,7 +3103,7 @@ namespace ts { | |
let type: Type; | ||
if (pattern.kind === SyntaxKind.ObjectBindingPattern) { | ||
if (declaration.dotDotDotToken) { | ||
if (!(parentType.flags & TypeFlags.Object)) { | ||
if (!isValidSpreadType(parentType)) { | ||
error(declaration, Diagnostics.Rest_types_may_only_be_created_from_object_types); | ||
return unknownType; | ||
} | ||
|
@@ -6102,11 +6110,25 @@ namespace ts { | |
* this function should be called in a left folding style, with left = previous result of getSpreadType | ||
* and right = the new element to be spread. | ||
*/ | ||
function getSpreadType(left: Type, right: Type, isFromObjectLiteral: boolean): ResolvedType | IntrinsicType { | ||
Debug.assert(!!(left.flags & (TypeFlags.Object | TypeFlags.Any)) && !!(right.flags & (TypeFlags.Object | TypeFlags.Any)), "Only object types may be spread."); | ||
function getSpreadType(left: Type, right: Type, isFromObjectLiteral: boolean): Type { | ||
if (left.flags & TypeFlags.Any || right.flags & TypeFlags.Any) { | ||
return anyType; | ||
} | ||
left = filterType(left, t => !(t.flags & TypeFlags.Nullable)); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this pattern |
||
if (left.flags & TypeFlags.Never) { | ||
return right; | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You can replace all of these new lines with just this: left = filterType(left, t => !(t.flags & TypeFlags.Nullable));
if (left.flags & TypeFlags.Never) {
return right;
}
right = filterType(right, t => !(t.flags & TypeFlags.Nullable));
if (right.flags & TypeFlags.Never) {
return left;
}
if (left.flags & TypeFlags.Union) {
return mapType(left, t => getSpreadType(t, right, isFromObjectLiteral));
}
if (right.flags & TypeFlags.Union) {
return mapType(right, t => getSpreadType(left, t, isFromObjectLiteral));
} |
||
right = filterType(right, t => !(t.flags & TypeFlags.Nullable)); | ||
if (right.flags & TypeFlags.Never) { | ||
return left; | ||
} | ||
if (left.flags & TypeFlags.Union) { | ||
return mapType(left, t => getSpreadType(t, right, isFromObjectLiteral)); | ||
} | ||
if (right.flags & TypeFlags.Union) { | ||
return mapType(right, t => getSpreadType(left, t, isFromObjectLiteral)); | ||
} | ||
|
||
const members = createMap<Symbol>(); | ||
const skippedPrivateMembers = createMap<boolean>(); | ||
let stringIndexInfo: IndexInfo; | ||
|
@@ -11438,7 +11460,7 @@ namespace ts { | |
typeFlags = 0; | ||
} | ||
const type = checkExpression((memberDecl as SpreadAssignment).expression); | ||
if (!(type.flags & (TypeFlags.Object | TypeFlags.Any))) { | ||
if (!isValidSpreadType(type)) { | ||
error(memberDecl, Diagnostics.Spread_types_may_only_be_created_from_object_types); | ||
return unknownType; | ||
} | ||
|
@@ -11516,6 +11538,12 @@ namespace ts { | |
} | ||
} | ||
|
||
function isValidSpreadType(type: Type): boolean { | ||
return !!(type.flags & (TypeFlags.Any | TypeFlags.Null | TypeFlags.Undefined) || | ||
type.flags & TypeFlags.Object && !isGenericMappedType(type) || | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. IIUC this should also fix #12460 |
||
type.flags & TypeFlags.UnionOrIntersection && !forEach((<UnionOrIntersectionType>type).types, t => !isValidSpreadType(t))); | ||
} | ||
|
||
function checkJsxSelfClosingElement(node: JsxSelfClosingElement) { | ||
checkJsxOpeningLikeElement(node); | ||
return jsxElementType || anyType; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
//// [restIntersection.ts] | ||
var intersection: { x: number, y: number } & { w: string, z: string }; | ||
|
||
var rest1: { y: number, w: string, z: string }; | ||
var {x, ...rest1 } = intersection; | ||
|
||
|
||
//// [restIntersection.js] | ||
var __rest = (this && this.__rest) || function (s, e) { | ||
var t = {}; | ||
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) | ||
t[p] = s[p]; | ||
if (s != null && typeof Object.getOwnPropertySymbols === "function") | ||
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) if (e.indexOf(p[i]) < 0) | ||
t[p[i]] = s[p[i]]; | ||
return t; | ||
}; | ||
var intersection; | ||
var rest1; | ||
var x = intersection.x, rest1 = __rest(intersection, ["x"]); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
=== tests/cases/compiler/restIntersection.ts === | ||
var intersection: { x: number, y: number } & { w: string, z: string }; | ||
>intersection : Symbol(intersection, Decl(restIntersection.ts, 0, 3)) | ||
>x : Symbol(x, Decl(restIntersection.ts, 0, 19)) | ||
>y : Symbol(y, Decl(restIntersection.ts, 0, 30)) | ||
>w : Symbol(w, Decl(restIntersection.ts, 0, 46)) | ||
>z : Symbol(z, Decl(restIntersection.ts, 0, 57)) | ||
|
||
var rest1: { y: number, w: string, z: string }; | ||
>rest1 : Symbol(rest1, Decl(restIntersection.ts, 2, 3), Decl(restIntersection.ts, 3, 7)) | ||
>y : Symbol(y, Decl(restIntersection.ts, 2, 12)) | ||
>w : Symbol(w, Decl(restIntersection.ts, 2, 23)) | ||
>z : Symbol(z, Decl(restIntersection.ts, 2, 34)) | ||
|
||
var {x, ...rest1 } = intersection; | ||
>x : Symbol(x, Decl(restIntersection.ts, 3, 5)) | ||
>rest1 : Symbol(rest1, Decl(restIntersection.ts, 2, 3), Decl(restIntersection.ts, 3, 7)) | ||
>intersection : Symbol(intersection, Decl(restIntersection.ts, 0, 3)) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
=== tests/cases/compiler/restIntersection.ts === | ||
var intersection: { x: number, y: number } & { w: string, z: string }; | ||
>intersection : { x: number; y: number; } & { w: string; z: string; } | ||
>x : number | ||
>y : number | ||
>w : string | ||
>z : string | ||
|
||
var rest1: { y: number, w: string, z: string }; | ||
>rest1 : { y: number; w: string; z: string; } | ||
>y : number | ||
>w : string | ||
>z : string | ||
|
||
var {x, ...rest1 } = intersection; | ||
>x : number | ||
>rest1 : { y: number; w: string; z: string; } | ||
>intersection : { x: number; y: number; } & { w: string; z: string; } | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
tests/cases/compiler/restInvalidArgumentType.ts(31,13): error TS2700: Rest types may only be created from object types. | ||
tests/cases/compiler/restInvalidArgumentType.ts(33,13): error TS2700: Rest types may only be created from object types. | ||
tests/cases/compiler/restInvalidArgumentType.ts(35,13): error TS2700: Rest types may only be created from object types. | ||
tests/cases/compiler/restInvalidArgumentType.ts(36,13): error TS2700: Rest types may only be created from object types. | ||
tests/cases/compiler/restInvalidArgumentType.ts(38,13): error TS2700: Rest types may only be created from object types. | ||
tests/cases/compiler/restInvalidArgumentType.ts(41,13): error TS2700: Rest types may only be created from object types. | ||
tests/cases/compiler/restInvalidArgumentType.ts(42,13): error TS2700: Rest types may only be created from object types. | ||
tests/cases/compiler/restInvalidArgumentType.ts(44,13): error TS2700: Rest types may only be created from object types. | ||
tests/cases/compiler/restInvalidArgumentType.ts(45,13): error TS2700: Rest types may only be created from object types. | ||
tests/cases/compiler/restInvalidArgumentType.ts(47,13): error TS2700: Rest types may only be created from object types. | ||
tests/cases/compiler/restInvalidArgumentType.ts(48,13): error TS2700: Rest types may only be created from object types. | ||
tests/cases/compiler/restInvalidArgumentType.ts(55,13): error TS2700: Rest types may only be created from object types. | ||
tests/cases/compiler/restInvalidArgumentType.ts(56,13): error TS2700: Rest types may only be created from object types. | ||
tests/cases/compiler/restInvalidArgumentType.ts(58,13): error TS2700: Rest types may only be created from object types. | ||
|
||
|
||
==== tests/cases/compiler/restInvalidArgumentType.ts (14 errors) ==== | ||
enum E { v1, v2 }; | ||
|
||
function f<T extends { b: string }>(p1: T, p2: T[]) { | ||
var t: T; | ||
|
||
var i: T["b"]; | ||
var k: keyof T; | ||
|
||
var mapped_generic: {[P in keyof T]: T[P]}; | ||
var mapped: {[P in "b"]: T[P]}; | ||
|
||
var union_generic: T | { a: number }; | ||
var union_primitive: { a: number } | number; | ||
|
||
var intersection_generic: T & { a: number }; | ||
var intersection_premitive: { a: number } | string; | ||
|
||
var num: number; | ||
var str: number; | ||
|
||
var u: undefined; | ||
var n: null; | ||
|
||
var a: any; | ||
|
||
var literal_string: "string"; | ||
var literal_number: 42; | ||
|
||
var e: E; | ||
|
||
var {...r1} = p1; // Error, generic type paramterre | ||
~~ | ||
!!! error TS2700: Rest types may only be created from object types. | ||
var {...r2} = p2; // OK | ||
var {...r3} = t; // Error, generic type paramter | ||
~~ | ||
!!! error TS2700: Rest types may only be created from object types. | ||
|
||
var {...r4} = i; // Error, index access | ||
~~ | ||
!!! error TS2700: Rest types may only be created from object types. | ||
var {...r5} = k; // Error, index | ||
~~ | ||
!!! error TS2700: Rest types may only be created from object types. | ||
|
||
var {...r6} = mapped_generic; // Error, generic mapped object type | ||
~~ | ||
!!! error TS2700: Rest types may only be created from object types. | ||
var {...r7} = mapped; // OK, non-generic mapped type | ||
|
||
var {...r8} = union_generic; // Error, union with generic type parameter | ||
~~ | ||
!!! error TS2700: Rest types may only be created from object types. | ||
var {...r9} = union_primitive; // Error, union with generic type parameter | ||
~~ | ||
!!! error TS2700: Rest types may only be created from object types. | ||
|
||
var {...r10} = intersection_generic; // Error, intersection with generic type parameter | ||
~~~ | ||
!!! error TS2700: Rest types may only be created from object types. | ||
var {...r11} = intersection_premitive; // Error, intersection with generic type parameter | ||
~~~ | ||
!!! error TS2700: Rest types may only be created from object types. | ||
|
||
var {...r12} = num; // Error | ||
~~~ | ||
!!! error TS2700: Rest types may only be created from object types. | ||
var {...r13} = str; // Error | ||
~~~ | ||
!!! error TS2700: Rest types may only be created from object types. | ||
|
||
var {...r14} = u; // OK | ||
var {...r15} = n; // OK | ||
|
||
var {...r16} = a; // OK | ||
|
||
var {...r17} = literal_string; // Error | ||
~~~ | ||
!!! error TS2700: Rest types may only be created from object types. | ||
var {...r18} = literal_number; // Error | ||
~~~ | ||
!!! error TS2700: Rest types may only be created from object types. | ||
|
||
var {...r19} = e; // Error, enum | ||
~~~ | ||
!!! error TS2700: Rest types may only be created from object types. | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: these two parts of code for left and right looks quite similar, can they be extracted to a function?