- Notifications
You must be signed in to change notification settings - Fork13.1k
Description
The spread type is a new type operator that types theTC39 stage 3 object spread operator. Its counterpart, the difference type, will type the proposed object rest destructuring operator. The spread type{ ...A, ...B } combines the properties, but not the call or construct signatures, of entities A and B.
The pull request is at#11150. The original issue for spread/rest types is#2103. Note that this proposal deviates from the specification by keeping all properties except methods, not just own enumerable ones.
Proposal syntax
The type syntax in this proposal differs from the type syntax as implemented in order to treat spread as a binary operator. Three rules are needed to convert the{ ...spread1, ...spread2 } syntax to binary syntaxspread1 ... spread2.
{ ...spread }becomes{} ... spread.{ a, b, c, ...d}becomes{a, b, c} ... d- Multiple spreads inside an object literal are treated as sequences of binary spreads:
{ a, b, c, ...d, ...e, f, g}becomes{a, b, c} ... d ... e ... { f, g }.
Type Relationships
- Identity:
A ... A ... Ais equivalent toA ... AandA ... Ais equivalent to{} ... A. - Commutativity:
A ... Bisnot equivalent toB ... A. Properties ofBoverwrite properties ofAwith the same name inA ... B. - Associativity:
(A ... B) ... Cis equivalent toA ... (B ... C)....is right-associative. - Distributivity: Spread is distributive over
|, soA ... (B | C)is equivalent toA ... B | A ... C.
Assignment compatibility
A ... Bis assignable toXif the properties and index signatures ofA ... Bare assignable to those ofX, andXhas no call or construct signatures.Xis assignable toA ... Bif the properties and index signatures ofXare assignable to those ofA ... B.
Type parameters
A spread type containing type parameters is assignable to another spread type if the type if the source and target types are both of the formT ... { some, object, type } and both source and target have the same type parameter and the source object type is assignable to the target object type.
Type inference
Spread types are not type inference targets.
Properties and index signatures
In the following definitions, 'property' means either a property or a get accessor.
The typeA ... B has a propertyP if
Ahas a propertyPorBhas a propertyP, and- Either
A.PorB.Pis not a method.
In this case(A ... B).P has the type
- Of
B.PifB.Pis not optional. - Of
A.P | B.PifB.Pis optional andAhas a propertyP. - Of
A.Potherwise.
private,protected andreadonly behave the same way as optionality except that ifA.P orB.P isprivate,protected orreadonly, then(A ...B).P isprivate,protected orreadonly, respectively.
Index signatures
The typeA ... B has an index signature ifA has an index signature andB has an index signature. The index signature's type is the union of the two index signatures' types.
Call and Construct signatures
A ... B has no call signatures and no construct signatures, since these are not properties.
Precedence
Precedence of... is higher than& and|. Since the language syntax is that of object type literals, precedence doesn't matter since the braces act as boundaries of the spread type.
Examples
Taken from theTC39 proposal and given types.
Shallow Clone (excluding prototype)
letaClone:{ ...A}={ ...a};
Merging Two Objects
letab:{ ...A, ...B}={ ...a, ...b};
Overriding Properties
letaWithOverrides:{ ...A,x:number,y:number}={ ...a,x:1,y:2};// equivalent toletaWithOverrides:{ ...A, ...{x:number,y:number}}={ ...a, ...{x:1,y:2}};
Default Properties
letaWithDefaults:{x:number,y:number, ...A}={x:1,y:2, ...a};
Multiple Merges
// Note: getters on a are executed twiceletxyWithAandB:{x:number, ...A,y:number, ...B, ...A}={x:1, ...a,y:2, ...b, ...a};// equivalent toletxyWithAandB:{x:number,y:number, ...B, ...A}={x:1, ...a,y:2, ...b, ...a};
Getters on the Object Initializer
// Does not throw because .x isn't evaluated yet. It's defined.letaWithXGetter:{ ...A,x:never}={ ...a,getx(){thrownewError('not thrown yet')}};
Getters in the Spread Object
// Throws because the .x property of the inner object is evaluated when the// property value is copied over to the surrounding object initializer.letruntimeError:{ ...A,x:never}={ ...a, ...{getx(){thrownewError('thrown now')}}};
Setters Are Not Executed When They're Redefined
letz:{x:number}={setx(){thrownewError();}, ...{x:1}};// No error
Null/Undefined Are Ignored
letemptyObject:{}={ ...null, ...undefined};// no runtime error
Updating Deep Immutable Object
letnewVersion:{ ...A,name:string,address:{address,zipCode:string},items:{title:string}[]}={ ...previousVersion,name:'New Name',// Override the name propertyaddress:{ ...previousVersion.address,zipCode:'99999'}// Update nested zip codeitems:[...previousVersion.items,{title:'New Item'}]// Add an item to the list of items};
Note: IfA = { name: string, address: { address, zipCode: string }, items: { title: string }[] }, then the type of newVersion is equivalent toA
Rest types
The difference type is the opposite of the spread type. It types the TC39 stage 3 object-rest destructuring operator. The difference typerest(T, a, b, c) represents the typeT after the propertiesa,b andc have been removed, as well as call signatures and construct signatures.
A short example illustrates the way this type is used:
/** JavaScript version */functionremoveX(o){let{ x, ...rest}=o;returnrest;}/** Typescript version */functionremoveX<Textends{x:number,y:number}>(o:T):rest(T,x){let{x, ...rest}:T=o;returnrest;}
Type Relationships
rest(A)is not equivalent toAbecause it is missing call and construct signatures.rest(rest(A))is equivalent torest(A).rest(rest(A, a), b)is equivalent torest(rest(A, b), a)andrest(A, a, b).rest(A | B, a)is equivalent torest(A, a) | rest(B, a).
Assignment compatibility
rest(T, x)is not assignable toT.Tis assignable torest(T, x)becauseThas more properties and signatures.
Properties and index signatures
The typerest(A, P) removesP fromA if it exists. Otherwise, it does nothing.
Call and Construct signatures
rest(A) does not have call or construct signatures.
Precedence
Difference types have similar precedence to- in the expression grammar, particularly compared to& and|. TODO: Find out what this precedence is.