Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Add spread/rest higher-order types operator #10727

Open
Labels
RevisitAn issue worth coming back toSuggestionAn idea for TypeScript
Milestone
@sandersn

Description

@sandersn

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.

  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 toA ... A andA ... A is equivalent to{} ... A.
  • Commutativity:A ... B isnot equivalent toB ... A. Properties ofB overwrite properties ofA with the same name inA ... B.
  • Associativity:(A ... B) ... C is equivalent toA ... (B ... C).... is right-associative.
  • Distributivity: Spread is distributive over|, soA ... (B | C) is equivalent toA ... B | A ... C.

Assignment compatibility

  • A ... B is assignable toX if the properties and index signatures ofA ... B are assignable to those ofX, andX has no call or construct signatures.
  • X is assignable toA ... B if the properties and index signatures ofX are 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

  1. A has a propertyP orB has a propertyP, and
  2. EitherA.P orB.P is not a method.

In this case(A ... B).P has the type

  1. OfB.P ifB.P is not optional.
  2. OfA.P | B.P ifB.P is optional andA has a propertyP.
  3. OfA.P otherwise.

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 toA because 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.
  • T is assignable torest(T, x) becauseT has 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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    RevisitAn issue worth coming back toSuggestionAn idea for TypeScript

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions


      [8]ページ先頭

      ©2009-2025 Movatter.jp