-
Notifications
You must be signed in to change notification settings - Fork 1.1k
More discussion of homomorphic mapped types #473
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -834,6 +834,10 @@ type Partial<T> = { [P in keyof T]?: T[P] } | |
|
|
||
| In these examples, the properties list is `keyof T` and the resulting type is some variant of `T[P]`. | ||
| This is a good template for any general use of mapped types. | ||
| That's because this kind of transformation is homomorphic, which means that the mapping applies to every property of `T` and no others. | ||
| The compiler knows that it can copy all the existing property modifiers before adding any new ones. | ||
| For example, if `Person.name` were readonly, `Partial<Person>.name` would be readonly and optional. | ||
|
|
||
| Here's one more example, in which `T[P]` is wrapped in a `Proxy<T>` class: | ||
|
|
||
| ```ts | ||
|
|
@@ -861,6 +865,16 @@ type Record<K extends string | number, T> = { | |
| } | ||
| ``` | ||
|
|
||
| `Readonly` and `Partial` are homomorphic whereas `Pick` and `Record` are not. | ||
|
||
| One clue that `Pick` and `Record` are not homomorphic is that they both take a union of property names: | ||
|
|
||
| ```ts | ||
| type ThreeStringProps = Record<'prop1' | 'prop2' | 'prop3', string> | ||
| type PersonJustName = Pick<Person, 'name'>; | ||
| ``` | ||
|
|
||
| These non-homomorphic types are essentially creating new properties (even though `Pick` uses `Person` as a source), so they don't copy property modifiers from anywhere; if `Person.name` were readonly, `Pick<Person, 'name'>.name` would not be readonly. | ||
|
|
||
| ## Inference from mapped types | ||
|
|
||
| Now that you know how to wrap the properties of a type, the next thing you'll want to do is unwrap them. | ||
|
|
@@ -878,8 +892,5 @@ function unproxify<T>(t: Proxify<T>): T { | |
| let originalProps = unproxify(proxyProps); | ||
| ``` | ||
|
|
||
| Note that this unwrapping inference works best on *homomorphic* mapped types. | ||
| Homomorphic mapped types are mapped types that iterate over every property of some type, and only those properties: `{ [P in keyof T]: X }`. | ||
| In the examples above, `Nullable` and `Partial` are homomorphic whereas `Pick` and `Record` are not. | ||
| One clue is that `Pick` and `Record` both take a union of property names in addition to a source type, which they use instead of `keyof T`. | ||
| Note that this unwrapping inference works best on homomorphic mapped types. | ||
|
||
| If the mapped type is not homomorphic you might have to explicitly give a type parameter to your unwrapping function. | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
might be useful to add a link to wikipedia's definition of Homomorphism: https://en.wikipedia.org/wiki/Homomorphism
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done