TypeScript: 型パラメータの魔法: infer キーワードの使い方
TypeScript: 型からジェネリックパラメータを抽出する方法
型からジェネリックパラメータを抽出する方法はいくつかありますが、最も一般的で強力な方法は、infer
キーワードを使用する方法です。infer
は、条件型と呼ばれる高度な型構文の一部であり、ジェネリック型から型パラメータを推論することができます。
以下に、infer
を使用して型からジェネリックパラメータを抽出する方法の例を示します。
type MyGeneric<T> = Array<T> & { length: number };
function identity<T>(value: T): T {
return value;
}
type ExtractedType = MyGeneric<infer U>; // U は `Array<T> & { length: number }` の T に相当
const myArray: MyGeneric<number> = [1, 2, 3];
const extractedValue: number = identity(myArray[0]); // extractedValue は number 型になる
この例では、MyGeneric
というジェネリック型を定義しています。この型は、Array<T>
と { length: number }
の交差点型です。つまり、MyGeneric
型の値は、配列であるだけでなく、length
プロパティを持つ必要があります。
次に、identity
というジェネリック関数定義します。この関数は、引数として渡された値をそのまま返します。
ExtractedType
という型エイリアスを使用して、MyGeneric
型から型パラメータ T
を抽出します。infer U
という構文を使用すると、U
は MyGeneric<T>
の T
に相当する型になります。
最後に、myArray
という変数に MyGeneric<number>
型の値を代入します。これは、数字の配列です。次に、identity
関数を使用して myArray
の最初の要素を抽出し、extractedValue
変数に代入します。extractedValue
は number
型になることがわかります。これは、identity
関数は T
を number
として扱っているためです。
この例は、infer
を使用して型からジェネリックパラメータを抽出する方法を単純化したものです。infer
は、より複雑なジェネリック型や条件型で使用することもできます。
infer
は、条件型と呼ばれる高度な型構文の一部です。条件型について詳しくは、TypeScript のドキュメントを参照してください。infer
を使用するには、TypeScript バージョン 4.0 以降が必要です。
type Identity<T> = (value: T) => T;
function identity<T>(value: T): T {
return value;
}
type ExtractedType = Identity<infer U>; // U は `T` に相当
const identityFunction: Identity<number> = identity;
const extractedValue: number = identityFunction(10); // extractedValue は number 型になる
オブジェクト型
type MyMap<K, V> = { [key: K]: V };
function mapKeys<K, V>(obj: MyMap<K, V>, fn: (key: K) => string): MyMap<string, V> {
const newObj: MyMap<string, V> = {};
for (const key in obj) {
newObj[fn(key)] = obj[key];
}
return newObj;
}
type ExtractedKeyType = MyMap<infer K, number>[keyof MyMap<K, number>]; // K は `MyMap<K, number>` の K に相当
const myMap: MyMap<string, number> = {
"a": 1,
"b": 2,
"c": 3,
};
const newMap: MyMap<string, number> = mapKeys(myMap, (key) => key.toUpperCase());
const extractedKey: ExtractedKeyType = Object.keys(newMap)[0]; // extractedKey は "A" になる
ジェネリック型
type MyGeneric<T> = Array<T> & { length: number };
function identity<T>(value: T): T {
return value;
}
type ExtractedType = MyGeneric<infer U>; // U は `Array<T> & { length: number }` の T に相当
const myArray: MyGeneric<number> = [1, 2, 3];
const extractedValue: number = identity(myArray[0]); // extractedValue は number 型になる
条件型
type MyConditional<T extends string> = T extends `${infer Prefix}_${infer Suffix}` ? { prefix: Prefix; suffix: Suffix } : never;
function splitString<T extends string>(str: T): MyConditional<T> {
const parts = str.split("_");
if (parts.length === 2) {
return { prefix: parts[0], suffix: parts[1] };
} else {
return never;
}
}
type ExtractedPrefixType = MyConditional<infer T>["prefix"]; // T は `${infer Prefix}_${infer Suffix}` に相当
const myString: string = "hello_world";
const splitResult: MyConditional<typeof myString> = splitString(myString);
const extractedPrefix: ExtractedPrefixType = splitResult.prefix; // extractedPrefix は "hello" になる
これらの例は、TypeScript で型からジェネリックパラメータを抽出する方法のほんの一例です。infer
キーワードと条件型を組み合わせることで、さまざまな複雑なジェネリック型から型パラメータを抽出することができます。
- TypeScript のジェネリック型と条件型に関する詳細は、TypeScript のドキュメントを参照してください。
型ガードを使用して、ジェネリック型の具体的な型パラメータを確認できます。ただし、この方法は infer
キーワードほど汎用性が高くありません。
type MyGeneric<T> = Array<T> & { length: number };
function identity<T>(value: T): T {
if (Array.isArray(value) && typeof value.length === "number") {
return value;
} else {
throw new Error("Invalid value");
}
}
const myArray: MyGeneric<number> = [1, 2, 3];
const extractedValue: number = identity(myArray[0]); // extractedValue は number 型になる
型アサーション
型アサーションを使用して、ジェネリック型の具体的な型パラメータを明示的に指定できます。ただし、この方法は冗長になる可能性があり、コードの読みやすさを損なう可能性があります。
type MyGeneric<T> = Array<T> & { length: number };
function identity<T>(value: T): T {
return value as MyGeneric<number>; // 型アサーションを使用する
}
const myArray: MyGeneric<number> = [1, 2, 3];
const extractedValue: number = identity(myArray[0]); // extractedValue は number 型になる
ヘルパー関数
ジェネリック型から型パラメータを抽出する専用のヘルパー関数を作成できます。ただし、この方法はコードを冗長にし、保守が難しくなる可能性があります。
type MyGeneric<T> = Array<T> & { length: number };
function getGenericParameter<T>(value: MyGeneric<T>): T {
return value[0]; // 最初の要素を返す
}
const myArray: MyGeneric<number> = [1, 2, 3];
const extractedValue: number = getGenericParameter(myArray); // extractedValue は number 型になる
これらの方法は、infer
キーワードよりも状況によっては使用しにくい場合がありますが、特定の状況では役立つことがあります。
infer
キーワードは、TypeScript で型からジェネリックパラメータを抽出する最も一般的で強力な方法です。ただし、他の方法も状況によっては役立つことがあります。
最適な方法は、特定のニーズと要件によって異なります。
- 上記の例は、説明を目的としたものであり、実際のアプリケーションで使用するには調整が必要になる場合があります。
typescript