概要
Objの中身をそれぞれ分解したObj2を作るにはどうすれば良いでしょうか。
type Obj = {
foo: string;
bar: number;
baz: boolean;
};
type Obj2 = { foo: string } | { bar: number } | { baz: boolean };
これが型の外の世界であれば、多くの方はmap
を使うかと思います。
const obj = {
foo: 'string',
bar: 100,
baz: false
};
const obj2 = Object.keys(obj).map(key => ({ [key]: obj[key] }));
// obj2 = [{ foo: 'string' }, { bar: 100 }, { baz: false }];
この記事では、このmap
っぽいことを型の世界で行う方法について説明して行きます。
やり方
TypeScriptにおけるin
TypeScriptにおけるin
はtrue
かfalse
を返すin演算子として説明されることが多いですが、もう1つの意味があります。
それはMapped typesです。
ref) Mapped types
これはまさに配列に対してmap
をかけるような処理となります。
const animals = ['dog', 'cat'];
const animalObject = animals.map(v => () => v);
// animalObject = {
// dog: () => 'dog',
// cat: () => 'cat',
// }
type Animal = 'dog' | 'cat';
type AnimalObject = { [V in Animal]: () => V };
// AnimalObject = {
// dog: () => 'dog',
// cat: () => 'cat',
// }
keyofとMapped types組み合わせる
このMapped typesは、keyof
と組み合わせることでオブジェクトの型それぞれに対して特定の処理を噛ませることができるようになります。
type User = {
id: string;
age: number;
name: string;
}
type UserProps = keyof User;
// UserProps = 'id' | 'age' | 'name'
type UserReadonly = { readonly [P in UserProps]: User[P] };
// type UserReadonly = {
// readonly id: string;
// readonly age: number;
// readonly name: string;
// }
これはUtility TypesのReadonly<T>
がやっていることと同じですね。
Partial<T>
やNullable<T>
など多くのUtility Typesがin
とkeyof
を組み合わせて使っているように、これは覚えておくと非常に便利な書き方です。
結合した後バラす
さて、|
で繋げられたそれぞれの型に対して特定の操作をしたいだけの場合はどうすれば良いでしょうか。
Mapped Typesを使うとそれぞれの型に対して操作ができます。
しかし結果は、1つのobjectとして結合されてしまいます。
type Animal = 'dog' | 'cat';
type AnimalObject = { [V in Animal]: () => V };
// AnimalObject = {
// dog: () => 'dog',
// cat: () => 'cat',
// }
そのため、Mapped Typesによって生成された結果を一段回掘ることで、元の次元に戻すことができます。
type AnimalFunctions = AnimalObject[Animal];
// type AnimalFunctions = () => 'dog' | () => 'cat';
これらを応用することで、様々な操作が可能となります。
回答
というわけで概要の問題については下記のように定義することができます。
type Obj = {
foo: string;
bar: number;
baz: boolean;
}
type Obj2 = { [T in keyof Obj]: Record<T, Obj[T]> }[keyof Obj]
// Obj2 = { foo: string } | { bar: number } | { baz: boolean }
社内Slackで質問があったので、記事にしてみました。