Open masaeedu opened 6 years ago
What does the type-level version of map look like for objects under this proposal?
@jack-williams Similar to the value-level map
for objects:
type Empty = {}
type WithKey<K, V, O> = { [k: K]: V, ...O }
type Map<F, O> =
O extends WithKey<infer K, infer V, infer O> ? WithKey<K, F<V>, Map<F, O>> :
O extends Empty ? Empty :
never
Search Terms
Mapped types, dependent typing, higher kinded types, type families
Suggestion
TypeScript currently has baked in support for mapped types, with special syntax for transforming type level maps and type level lists (tuples). Borrowing an example from #26063:
The logic for when a type is treated as a mappable type unto itself, and when it is simply treated as an heterogeneous map for the purposes of type mapping is built out of various edge cases.
The code above is analogous at the value level to writing the following:
The
MAP{ [p in t]: box(t[p]) }
thing above stands out as something of an oddity; you don't usually see a "map operator" built directly into a language. Given a way to inductively break down and reconstruct data, users are perfectly capable of defining amap
function for each type of data themselves:Similarly, for maps:
The important thing to notice here is that once we have a way to break down and reconstruct the data recursively, we don't need any language-level features to express mapping over the data; both implementations of
map
require only the ability to define and apply functions, and for such functions to themselves be valid arguments and results (i.e. the features found in a minimal lambda calculus).Moving back to the type level, we can promote our value level implementation of
map
to a type constructor in pseudo-TypeScript:Using the ability to abstract over type level functions and to apply them, i.e. utilizing the lambda calculus at the type level, the user is able to define "mapped types" without the need for this to be implemented in the language.
In fact, mapped types (i.e. structure preserving transformations), are just one useful tool for massaging types. It would similarly be useful for the user to be able to fold down a type level list into a type level union (using the binary
|
type constructor), or to fold down a type level list of object types into their intersection (using the binary&
type constructor).AFAICT the primary missing features here are HKTs (#1213) and recursive type constructors (#6230), but it's possible there are other features related to spread types, conditional types, and
infer
that are missing. This provides good motivation for implementing these features in a sound way; it would allow powerful abstractions like mapped types to be implemented and extended in libraries rather than in the core language.Checklist
My suggestion meets these guidelines: