microsoft / microsoft/TypeScript
Keyword for inferring a new type from destructured parameters
Nessuno ha ancora preso questa issue.
- Lingua principale
- Go
- Stelle
- 111k
- Fork
- 14.3k
- Merge medio
- 1g 19h
- PR unite (30g)
- 117
Descrizione
🔍 Search Terms
destructuring, destruct, infer destruct, infer destructuring, function destruct, function destructuring
Issue https://github.com/microsoft/TypeScript/issues/42419 (+ https://github.com/microsoft/TypeScript/issues/48220) discusses changing the current behavior of TypeScript to address the same underlying issue.
My proposal is different as it introduces a new explicit language feature and does not change any current behavior.
I also found https://github.com/microsoft/TypeScript/issues/7576. The essence here is that they want some sort of shorthand for destructured parameter types.
I think this is vaguely related to my proposal since it addresses a way to do that—it touches on a similar pain point.
✅ Viability Checklist
- 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, new syntax sugar for JS, etc.)
- This feature would agree with the rest of our Design Goals: https://github.com/Microsoft/TypeScript/wiki/TypeScript-Design-Goals
⭐ Suggestion
Let's introduce a feature that allows for TypeScript to infer a new type from destructured function parameters.
To conceptualize this idea, I'm introducing a new keyword called from which can be used after : when destructuring a function parameter:
type Person = { first: string, last: string, age: number };
const isAdult = ({ age }: from Person) => age > 18;
This declares that typeof isAdult is (person: Pick<Person, "age">) => ... instead of (person: Person) => ... meaning that any object matching { age: number } can be passed in as a parameter.
You could also potentially do this when destructuring a local variable, though, I struggle to see its usefulness:
const { age, first }: from Person = person;
I'm not even sure whether this should be legal.
📃 Motivating Example
We often use destructuring in functions, because it helps us stay DRY:
type Person = { first: string, last: string, age: number };
const formatName = ({ first, last }: Person) => `${first} ${last}`;
const isAdult = ({ age }: Person) => age > 18;
I think this is good practice and a sensible thing to include in your company style guide.
But if we look critically at the above snippet, we can see that the signatures for both formatName and isAdult are lying about their implementation. They (quite excessively) declare that they need all properties from Person when they actually only need a few. And rightly so: I have explicitly declared that by writing : Person, but my intention here was actually to be able to pass any object that matches { age: number } into isAdult (for example).
To solve this, I have to jump through some hoops, either like this …
type Person = { first: string, last: string, age: number };
const isAdult = ({ age }: Pick<Person, "age">) => age > 18;
const formatName = ({ first, last }: Pick<Person, "first" | "last">) => `${first} ${last}`;
... or the inverse:
type Named = { first: string, last: string };
type Aged = { age: number; }
type Person = Named & Aged;
const formatName = ({ first, last }: Named) => `${first} ${last}`;
const isAdult = ({ age }: Aged) => age > 18;
However…
… the first solution is noisy and un-DRY since have to write the "pick part" twice: first for the destructuring itself, and then for the Pick type. Note: this effect is greatly exacerbated as the number of properties of a type increases, so this example is quite tame in that regard.
… the second solution (workaround?) feels too verbose for my use case (there's too much detail), I have no intent of re-use for the Aged or Named type. If the Person type comes from a third-party library (or another source that I don't control, e.g. Prisma), then this option is out of the question entirely as I can't "build up" my own type from smaller fragments (i.e. Person = Aged & Named).
This means that the first solution is often the only viable option—and it just isn't a great experience.
So—with the addition of the from keyword (or something similar), this headache is entirely relieved! It would make it much more convenient to declare the explicit requirements of a function to effortlessly make your code more expressive.
💻 Use Cases
- What do you want to use this for?
This would have great utility in React components where you just want to pick a few props out of e.g. ButtonProps from @mui/material/Button:
function FooterButton({ color, href, children }: from ButtonProps)
However, I see many use cases for this: Anywhere where you need more generically defined function type signatures without having to create a new explicit type or use Pick/Omit.
- What shortcomings exist with current approaches?
You need to manually use Pick/Omit or break types into smaller (often hard-to-name) types and compose them later on, or just live with the "excessive" type definitions.
- What workarounds are you using in the meantime?
The above 👆
Guida per i contributori
Apri la guida per i contributori
Come iniziare
- Leggi tutta la issue e poi la guida ai contributi del progetto.
- Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
- Fai un fork del repository e lavora su un branch.
- Apri una pull request che faccia riferimento al numero della issue.
Direzione di ricerca
Inizia leggendo le issue collegate 42419, 48220 e 7576, quindi confronta le relative discussioni con la sintassi from proposta e il comportamento esistente dei parametri destrutturati. Chiarisci se l'ambito comprende solo i parametri delle funzioni, come dovrebbe comportarsi il tipo Pick inferito e quali test di sintassi e controllo dei tipi dimostrerebbero il completamento.
Scritto dal modello di indicizzazione a partire dal testo della issue.
Valutazione
- Stack tecnologico
- typescript
- Ambito
- compilers
- Tipo di issue
- Funzionalità
- Difficoltà
- 5/5
- Tempo stimato
- Più di una settimana
- Stato di attività
- Tranquilla
- Chiarezza
- Abbastanza chiara
- Idoneità per principianti
- 35/100