🐶
TypescirptのRecord<T, K>について理解する
背景
typescriptでRecord<T, K>を使う場面に遭遇したので備忘録として記録しておく。
状況
あるオブジェクトのキーを特定の文字列に変換する関数translateを作成していました。
translate
const target = {
name: "hoge",
age: 20,
}
const translateText = {
name: "名前",
age: "年齢",
}
const result = translate(target, translateText)
// result = {
// 名前: "hoge",
// 年齢: 20,
// }
translate関数を型安全にするために、以下のように型付けしました。
translate
const translate = async <T extends Object>(
target: T,
translateText: {[key: keyof T]: string}
) => {};
targetをT型のオブジェクトに制限しつつ、translateTextはtargetのkeyをkeyに持つオブジェクトに制限したかったのです。
しかし、コンパイラに以下のように怒られました。
An index signature parameter type cannot be a literal type or generic type. Consider using a mapped object type instead.ts(1337)
(parameter) key: keyof T
typescriptではインデックスシグネチャはstring型かnumber型しか許されていないのに、リテラル型を渡しているのが原因みたいです。
さてどうしたものかと色々調べているとRecord<T, K>という組み込みの型があること知りました。
Record<T, K>について
Record<T, K>の型定義を見てみると以下のようになっています。
lib.es5.d.ts
/**
* Construct a type with a set of properties K of type T
*/
type Record<K extends keyof any, T> = {
[P in K]: T;
};
つまり、あるオブジェクトのキーがKで、それぞれのキーに対応する値がTの型を持つことを保証します。
今回の例で言うとtargetのキーである、nameとageの型を任意の型Tであると保証できるのです。
まさに今回のような場合にはピッタリです。
結論
最終的には以下のコードになりました。
translate
const translate = async <T extends Object>(
target: T,
translateText: Record<keyof T, string>
) => {};
これで
targetをT型のオブジェクトに制限しつつ、translateTextはtargetのkeyをkeyに持つオブジェクトに制>限したかった
と言う問題を解決できました。
めでたしめでたし。
Discussion