Skip to content

Proposal: Add a strictly typed spread operator #30826

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

Closed
5 tasks done
vbfox opened this issue Apr 9, 2019 · 3 comments
Closed
5 tasks done

Proposal: Add a strictly typed spread operator #30826

vbfox opened this issue Apr 9, 2019 · 3 comments
Labels
Suggestion An idea for TypeScript Too Complex An issue which adding support for may be too complex for the value it adds

Comments

@vbfox
Copy link

vbfox commented Apr 9, 2019

Search Terms

  • spread
  • strict spread

There are a few related issues closed with a link to #12632 (For example #17422) but it's a lot more generic, this proposal is very specific to the spread operator where I believe there is a relatively simple fix that would handle a lot of situations.

Suggestion

Add an operator similar to the spread operator that ensure that the type of the resulting object is the same as the type of the input one.

It would serve the exact same purpose as the with operator in F#, the | operator in Elm or the ... operator in ReasonML.

For example:

interface State {
    readonly id: number,
    readonly validated: boolean
}

// if the operator is `....` (same as ReasonML with an additional dot) : 
const state: State = { id: 0, validated: false };
const newState = { ....state, validated: true }; // newState: State
const newState2 = { ....state, failed: true }; // Compilation error, 'failed' isn't in the keys of State

The following limitations would apply:

  • The operator is only valid in the first position of an anonymous object build with spread
  • The target type can be created by an anonymous object (so an interface not a class)

It would be compiled to ... as the difference is only relative to typechecking

Use Cases

The central use case is to mutate objects that are used as immutable like React state or Redux state.

While it currently work most of the time as in the end the signature generated by the spread operator will be matched against the real type it produce problems:

  • Errors are often shown far away from where the problem really is, requiring to hunt what function actually generated a type that had an invalid property
  • Any function returning the result of a spread has a hard to read signature, often for nothing as an interface matching it exists
  • The solution is often to sprinkle manual type annotations everywhere that would be unnecessary if the type wasn't lost in the first place

Examples

interface State {
    readonly id: number,
    readonly validated: boolean
}

// State => State
const validate = (s: State) => {....s, validated: true};

const state: State = { id: 0, validated: false };
const newState = { ....state, id: 42 }; // : State
const validatedState = validate(newState); // : State

Would also work for anonymous types :

const state = { id: 0, validated: false };
const newState = { ....state, id: 42 }; // Ok
const newState2 = { ....state, isInvalid: true }; // Compilation error, 'isInvalid' isn't in the keys of '{ id: number, validated: boolean }'

Checklist

My suggestion meets these guidelines:

  • This wouldn't be a breaking change in existing TypeScript/JavaScript code
  • This wouldn't change the runtime behavior of existing JavaScript code
  • This could be implemented without emitting different JS based on the types of the expressions
  • This isn't a runtime feature (e.g. library functionality, non-ECMAScript syntax with JavaScript output, etc.)
  • This feature would agree with the rest of TypeScript's Design Goals.
@ajafff
Copy link
Contributor

ajafff commented Apr 9, 2019

You can already achieve that using explicit type annotations :

const newState2: typeof state= { ....state, isInvalid: true }; // complains about excess property 

Your original request is probably better implemented as a linter rule. IMO it's too specific to your workflow to be part of the compiler.

@RyanCavanaugh RyanCavanaugh added Suggestion An idea for TypeScript Too Complex An issue which adding support for may be too complex for the value it adds labels Apr 9, 2019
@RyanCavanaugh
Copy link
Member

This doesn't seem enough better than the existing options to warrant adding new expression-level syntax.

@typescript-bot
Copy link
Collaborator

This issue has been marked as "Too Complex" and has seen no recent activity. It has been automatically closed for house-keeping purposes.

@typescript-bot typescript-bot closed this as not planned Won't fix, can't repro, duplicate, stale Jun 21, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Suggestion An idea for TypeScript Too Complex An issue which adding support for may be too complex for the value it adds
Projects
None yet
Development

No branches or pull requests

4 participants