Conversation
Sign up for free
to subscribe to this conversation on GitHub.
Already have an account?
Sign in.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Leaves #10727 still open
Fixes #2103
Leaves #11100 still open
Important Update
The PR no longer contains a spread type, only spread syntax, which creates object types. I expect a spread type or something similar to be available in future versions of TypeScript.
Updates to proposed semantics: type variable assignability
A spread type containing spread elements whose types are type variables is assignable to another spread type also containing type variables if the non-type-variable properties and index signatures are assignable as above and the the type variables are the same variables in the same order.
For example:
{ a: number, ...T, b: number, ...U, ...x }is assignable to{ b: number, a: number, ...x ...T, ...U}and vice versa. But the similar{ a: number, ...T, b: number, ...U, ...x }is not assignable to{ b: number, a: number, ...x ...U, ...T}because the order ofTandUis reversed.This rule is not strictly safe, since a type parameter could override any properties that precede it (see the rule for
privatebelow), but it would be odd if{ ...T, ...U }were not assignable to{ ...T, ...U }. Instead, this rule assumes that the contents of a type variable will not conflict with other properties.Important Implementation Details
Spread types use a binary representation to make recursive transformations easier. This matches the proposal but the syntax remains close to spread's expression syntax.
getSpreadTypecreates a spread only if the spread type contains a type parameter. Otherwise it will create an anonymous type, or union or intersection of anonymous types. The binary structure is left deep, so the left side of a spread type is either another spread (the recursive case) or an object type (the terminal case). The right side of spread type is either a type parameter or an object type. Adjacent object types are spread into a single object type, so the structure will never have two adjacent object types, only adjacent type parameters.Updated proposal
The spread part of #10727's updated proposal follows for convenience:
The spread type is a new type operator that types the TC39 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 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
{ ...spread }syntax to binary syntaxspread1 ... spread2.{ ...spread }becomes{} ... spread.{ a, b, c, ...d}becomes{a, b, c} ... d{ a, b, c, ...d, ...e, f, g}becomes{a, b, c} ... d ... e ... { f, g }.Type Relationships
{} ... Ais equivalent to the properties ofA.A ... A ... Ais equivalent toA ... AandA ... Ais equivalent to{} ... A.A ... Bis not equivalent toB ... A. Properties ofBoverwrite properties ofAwith the same name.(A ... B) ... Cis equivalent toA ... (B ... C)....is right-associative.|and&, soA ... (B | C)is equivalent toA ... B | A ... CandA ... (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 variables
A spread type containing type parameters is assignable to another spread type if the the non-type-variable properties and index signatures are assignable as above and the type variables are the same variables in the same order.
For example:
{ a: number, b: number } ... Tis assignable to{ a: number } ...Tand toT ... { a: number }. But the similarT ... Uis not assignable toU ... Tbecause the order ofTandUis reversed.Type inference
Type inference is allowed between an object type source and a spread type target with a type parameter as either the left or right side. If the type parameter is on the right, then its inferred type is the whole source type. If the type parameter is on the left, then its inferred type is the source type minus properties on the right side of the spread.
For example, if the target is
T ... { a: number }and the source is{ a: number, b: string }, the inferred type forTis{ b: string }. Because spread types remove call and construct signatures, if the source were{ a: number, b: string, (): void }, the type inferred forTwould still be{ b: string }.Properties and index signatures
In the following definitions, 'property' means either a property or a get accessor.
The type
A ... Bhas a propertyPifAhas a propertyPorBhas a propertyP. In this case(A ... B).Phas the typeB.PifB.Pis not optional.A.P | B.PifB.Pis optional andAhas a propertyP.A.Potherwise.Index signatures spread the same way as properties.
private,protectedandreadonlybehave the same way as optionality except that ifA.PorB.Pisprivate,protectedorreadonly, then(A ...B).Pisprivate,protectedorreadonly, respectively.Call and Construct signatures
A ... Bhas 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 the TC39 proposal and given types.
Shallow Clone (excluding prototype)
Merging Two Objects
Overriding Properties
Default Properties
Multiple Merges
Getters on the Object Initializer
Getters in the Spread Object
Setters Are Not Executed When They're Redefined
Null/Undefined Are Ignored
Updating Deep Immutable Object
Note: If
A = { name: string, address: { address, zipCode: string }, items: { title: string }[] }, then the type of newVersion is equivalent toA