Skip to content
This repository has been archived by the owner on Oct 12, 2022. It is now read-only.

More discussion of homomorphic mapped types #473

Merged
merged 4 commits into from
Jan 4, 2017
Merged
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 15 additions & 5 deletions pages/Advanced Types.md
Original file line number Diff line number Diff line change
Expand Up @@ -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](https://en.wikipedia.org/wiki/Homomorphism), which means that the mapping applies only to properties 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
Expand Down Expand Up @@ -861,6 +865,15 @@ type Record<K extends string | number, T> = {
}
```

`Readonly`, `Partial` and `Pick` are homomorphic whereas `Record` is not.
One clue that `Record` is not homomorphic is that it doesn't take an input type to copy properties from:

```ts
type ThreeStringProps = Record<'prop1' | 'prop2' | 'prop3', string>
```

Non-homomorphic types are essentially creating new properties, so they can't copy property modifiers from anywhere.

## 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.
Expand All @@ -878,8 +891,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`.
If the mapped type is not homomorphic you might have to explicitly give a type parameter to your unwrapping function.
Note that this unwrapping inference only works on homomorphic mapped types.
If the mapped type is not homomorphic you will have to explicitly give a type parameter to your unwrapping function.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

give an explicit type parameter.