Announcing: Mapped Types + Pick, Omit, and Record
tl;dr: Mapped types allow you to transform object types into other object types. We have added definitions for Pick
, Omit
, and Record
utility types to our library definitions. Docs here.
Mapped Types are enabled by default as of Flow v0.212. To use them, make sure you are following our recommended setup in our docs. Mapped Types (inspired by TypeScript) provide an elegant syntax for transforming object types:
type Obj = {
foo: number,
bar: string,
baz: boolean,
};
type Accessor<+T> = () => T;
type ObjWithAccessors = {[key in keyof Obj]: Accessor<Obj[key]>};
function f(o: ObjWithAccessors): number {
return o.foo();
}
In this example, ObjWithAccessors
is a Mapped Type that turns each property of Obj
into a function returning the type defined in Obj
. It does this all without requiring you to list out the property names again, and changes to the definition of Obj
will be reflected in ObjWithAccessors
.
We’ve also implemented the highly requested utility types Pick
, Omit
, and Record
using Mapped Types and Conditional Types. We’ve added them to our library definitions.
Mapped Types bring several improvements over $ObjMap
, our previous solution for mapping object types:
- The syntax is cleaner and easier to both read and write
- The expressive power is increased, allowing you to add optionality and variance modifiers in addition to the value type transformation
- The underlying representation is crisp and principled, which creates a more reliable and consistent experience
You can find documentation for the feature on our website.
Basic Usage
Mapped Types have syntax similar to indexed object types but use the in
keyword:
type O = {foo: number, bar: string};
type Boxify<T> = {contents: T};
type MappedType = {[key in keyof O]: Boxify<O[key]>};
Combined with the keyof
keyword, MappedType
has all of the keys from O
with all of the value types transformed by Boxify<O[key]>
. The key
variable is substituted for each key in O
when creating the property, so this type evaluates to:
{
foo: Boxify<O['foo']>,
bar: Boxify<O['bar']>,
}
= {
foo: {contents: number},
bar: {contents: string},
}
In addition to transforming the value types, you can also add optionality and variance modifiers:
type ReadOnlyOptionalBoxes = {+[key in keyof O]?: Boxify<O[key]>};
// = {+foo?: {contents: number}, +bar?: {contents: string}
Generating Objects from Keys
You can also use mapped types to turn a string literal union into an object type by using in with a union of valid key types (no keyof
needed!)
type Union = 'mapped' | 'types' | 'rock';
type Obj = {[key in Union]: number};
// = {mapped: number, types: number, rock: number}