mosya<TC> - Merge型を作って二つのオブジェクト型をマージしようの解説
この記事はmosya<TC>の問題の一つであるMerge型の解説になります。
問題
2 つの型をマージして新しい型を作ります。2 つ目に指定した型のキーは 1 つ目の型のキーを上書きします。
例えば以下の条件を満たすような型を作りましょう。
type foo = {
name: string;
age: string;
};
type coo = {
age: number;
sex: string;
};
type Result = Merge<foo, coo>; // expected to be {name: string, age: number, sex: string}
前提知識
この問題を解くにあたって型についての以下の知識を理解しておく必要があります。
Conditional Typesを理解するkey ofを理解するMapped Typesを理解する- インデックスアクセス型を理解する
Conditional Typesを理解する
Conditional Typesは、条件によって型を変更することができる機能です。
例えば、以下のような型が考えられます。
type Foo<T> = T extends string
? string
: number;
この型は、Tがstring型を継承している場合はstring型を、そうでない場合はnumber型を返します。
このように、extendsを使って条件を指定することで、型を変更することができます。
Mapped Typesを理解する
Mapped Typesはユニオン型を使って、新しいオブジェクトの型を生成する機能です。
例えば、以下のようなユニオン型があったとします。
type TodoKeys = "title" | "description";
このユニオン型を使って、以下のようなオブジェクトの型を生成することができます。
type Todo = {
[K in TodoKeys]: string;
};
// 以下のように展開される
type Todo = {
title: string;
description: string;
};
key ofを理解する
key ofは、オブジェクトのキーをユニオン型として取得する機能です。
例えば、以下のようなオブジェクトがあったとします。
type Todo = {
title: string;
description: string;
};
このオブジェクトのキーを取得するには、以下のようにします。
type TodoKeys = keyof Todo; // "title" | "description"
インデックスアクセス型を理解する
インデックスアクセス型は、オブジェクトのプロパティにアクセスするための機能です。
例えば、以下のようなオブジェクトがあったとします。
interface Todo {
title: string;
description: string;
}
このオブジェクトのプロパティにアクセスするには、以下のようにします。
type Title = Todo["title"]; // string
このように、Todo["title"]とすることで、Todoのtitleプロパティの型を取得することができます。
これをインデックスアクセス型と呼びます。
先ほど登場したMapped Typesと組み合わせることで、一つずつオブジェクトのプロパティにアクセスすることができます。
便利な組み込み型 Exclude を理解する
Excludeは、ユニオン型から特定の型を除外することができる便利な組み込み型です。
例えば、以下のようなユニオン型があったとします。
type Foo = string | number | boolean;
このユニオン型からstring型を除外するには、以下のようにします。
type Bar = Exclude<Foo, string>; // number | boolean
解答例
以上の知識を使って、以下のように解答することができます。
type Merge<F, S> = {
[K in
| keyof F
| keyof S]: K extends keyof S
? S[K]
: K extends keyof F
? F[K]
: never;
};
まず、keyof Fとkeyof Sを使って、FとSのオブジェクトのキーをユニオン型として取得します。
これによって二つのオブジェクトのキー一覧をユニオン型として取得することができます。
次に、そのキーを使って、FとSのキー一覧から新しいオブジェクトの型を生成します。
type Merge<F, S> = {
[K in keyof F | keyof S]: ...
}次にこのキーを意味するKがSに含まれるキーなのか、Fに含まれるキーなのかを判定し、条件ごとに型を変更します。
type Merge<F, S> = {
[K in
| keyof F
| keyof S]: K extends keyof S
? S[K]
: K extends keyof F
? F[K]
: never;
};
もし、Kがオブジェクト型のSに含まれるキーであれば、S[K]を返し、オブジェクト型のFに含まれるキーであれば、F[K]を返します。
Authored by

Godai@steelydylan
Webサービスを作るのが好きなWebエンジニア。子供が産まれたことをきっかけに独立し法人化。サービス開発が大好き。
好きな言語はTypeScript。
Related









