Repository: TypeScript
Issue: Add spread/rest higher-order types operator
Labels: Suggestion,Revisit

The spread type is a new type operator that types the [TC39 stage 3 object spread operator](https://github.com/sebmarkbage/ecmascript-rest-spread). 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 syntax `spread1 ... spread2`.
1. `{ ...spread }` becomes `{} ... spread`.
2. `{ a, b, c, ...d}` becomes `{a, b, c} ... d`
3. 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 ... A` is equivalent to `A ... A` and `A ... A` is equivalent to `{} ... A`.
- Commutativity: `A ... B` is _not_ equivalent to `B ... A`. Properties of `B` overwrite properties of `A` with the same name in `A ... B`.
- Associativity: `(A ... B) ... C` is equivalent to `A ... (B ... C)`. `...` is right-associative.
- Distributivity: Spread is distributive over `|`, so `A ... (B | C)` is equivalent to `A ... B | A ... C`.
## Assignment compatibility
- `A ... B` is assignable to `X` if the properties and index signatures of `A ... B` are assignable to those of `X`, and `X` has no call or construct signatures.
- `X` is assignable to `A ... B` if the properties and index signatures of `X` are assignable to those of `A ... 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 form `T ... { 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 type `A ... B` has a property `P` if 
1. `A` has a property `P` or `B` has a property `P`, and
2. Either `A.P` or `B.P` is not a method. 

In this case `(A ... B).P` has the type 
1. Of `B.P` if `B.P` is not optional.
2. Of `A.P | B.P` if `B.P` is optional and `A` has a property `P`.
3. Of `A.P` otherwise.

`private`, `protected` and `readonly` behave the same way as optionality except that if `A.P` or `B.P` is `private`, `protected` or `readonly`, then `(A ...B).P` is `private`, `protected` or `readonly`, respectively.
## Index signatures

The type `A ... B` has an index signature if `A` has an index signature and `B` 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 the [TC39 proposal](https://github.com/sebmarkbage/ecmascript-rest-spread/blob/master/Spread.md) and given types.
### Shallow Clone (excluding prototype)

``` ts
let aClone: { ...A } = { ...a };
```
### Merging Two Objects

``` ts
let ab: { ...A, ...B } = { ...a, ...b };
```
### Overriding Properties

``` ts
let aWithOverrides: { ...A, x: number, y: number } = { ...a, x: 1, y: 2 };
// equivalent to
let aWithOverrides: { ...A, ...{ x: number, y: number } } = { ...a, ...{ x: 1, y: 2 } };
```
### Default Properties

``` ts
let aWithDefaults: { x: number, y: number, ...A } = { x: 1, y: 2, ...a };
```
### Multiple Merges

``` ts
// Note: getters on a are executed twice
let xyWithAandB: { x: number, ...A, y: number, ...B, ...A } = { x: 1, ...a, y: 2, ...b, ...a };
// equivalent to
let xyWithAandB: { x: number, y: number, ...B, ...A } = { x: 1, ...a, y: 2, ...b, ...a };
```
### Getters on the Object Initializer

``` ts
// Does not throw because .x isn't evaluated yet. It's defined.
let aWithXGetter: { ...A, x: never } = { ...a, get x() { throw new Error('not thrown yet') } };
```
### Getters in the Spread Object

``` ts
// Throws because the .x property of the inner object is evaluated when the
// property value is copied over to the surrounding object initializer.
let runtimeError: { ...A, x: never } = { ...a, ...{ get x() { throw new Error('thrown now') } } };
```
### Setters Are Not Executed When They're Redefined

``` ts
let z: { x: number } = { set x() { throw new Error(); }, ...{ x: 1 } }; // No error
```
### Null/Undefined Are Ignored

``` ts
let emptyObject: {} = { ...null, ...undefined }; // no runtime error
```
### Updating Deep Immutable Object

``` ts
let newVersion: { ...A, name: string, address: { address, zipCode: string }, items: { title: string }[] } = {
  ...previousVersion,
  name: 'New Name', // Override the name property
  address: { ...previousVersion.address, zipCode: '99999' } // Update nested zip code
  items: [...previousVersion.items, { title: 'New Item' }] // Add an item to the list of items
};
```

Note: If `A = { name: string, address: { address, zipCode: string }, items: { title: string }[] }`, then the type of newVersion is equivalent to `A`
# Rest types

The difference type is the opposite of the spread type. It types the TC39 stage 3 object-rest destructuring operator.  The difference type `rest(T, a, b, c)` represents the type `T` after the properties `a`, `b` and `c` have been removed, as well as call signatures and construct signatures. 

A short example illustrates the way this type is used:

``` ts
/** JavaScript version */
function removeX(o) {
  let { x, ...rest } = o;
  return rest;
}

/** Typescript version */
function removeX<T extends { x: number, y: number }>(o: T): rest(T, x) {
  let { x, ...rest }: T = o;
  return rest;
}
```
## Type Relationships
- `rest(A)` is not equivalent to `A` because it is missing call and construct signatures. 
- `rest(rest(A))` is equivalent to `rest(A)`. 
- `rest(rest(A, a), b)` is equivalent to `rest(rest(A, b), a)` and `rest(A, a, b)`.
- `rest(A | B, a)` is equivalent to `rest(A, a) | rest(B, a)`.
## Assignment compatibility
- `rest(T, x)` is not assignable to `T`.
- `T` is assignable to `rest(T, x)` because `T` has more properties and signatures.
## Properties and index signatures

The type `rest(A, P)` removes `P` from `A` 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.