2022年4月5日 りあクト! 第4章 TypeScriptで型をご安全に 4-5 組み込みユーティリティ型(p.195~)
続: 組み込みユーティリティ型
NonNullableは型引数に渡した型からnull undefinedを取り除いてくれます。
NonNullable<T>
type T1 = NonNullable<string | number | undefined>; type T2 = NonNullable<number[] | null | undefined>; const str:T1 = undefined; //compileerror! const arr:T2 = null; // compile error!
Record<K, T>
Recordは第一型引数に共用体型を指定、第二型引数に任意の型を指定することでオブジェクトの型が作成出来ます。
type Animal = 'cat' | 'dog' | 'rabbit'; type AnimalNote = Record<Animal, string>; const animalKanji: AnimalNote = { cat: '猫', dog: '犬', rabbit: '兎', };
Parameters<T>
、 ReturnType<T>
Parameters<T>
は、関数の型を型引数に指定することで、その関数の引数の型を抽出し、タプル型で返します。
ReturnType<T>
は、関数の型を型引数に指定することで、その関数の戻り値の型を返します。
const f1 = (a: number, b: string) => { console.log(a, b) }; const f2 = () => ({x: 'hello', y: true}); type P1 = Parameters<typeof f1>; // [a: number, b: string] type P2 = Parameters<typeof f2>; // [] type R1 = ReturnType<typeof f1>; // void type R2 = ReturnType<typeof f2>; // type R2 = { // x: string; // y: boolean; // }
Uppercase<T>
Lowercase<T>
Capitaleze<T>
Upcapitalize<T>
- Uppercase
<T>
...... Tの各要素の文字列をすべて大文字にします。 - Lowercase
<T>
...... Tの各要素の文字列をすべて小文字にします。 - Capitalize
<T>
...... Tの各要素の文字列の頭を大文字にします。 - Uncapitalize
<T>
...... Tの各要素の文字列の頭を小文字にします。
type Company = 'Apple' | 'IBM' | 'GitHub'; type C1 = Lowercase<Company>; // 'apple' | 'ibm' | 'github' type C2 = Uppercase<Company>; // 'IBM' | 'APPLE' | 'GITHUB' type C3 = Uncapitalize<Company>; // 'apple' | 'iBm' | 'gitHub' type C4 = Capitalize<C3>; // 'Apple' | 'IBM' | 'GitHub'
関数のオーバーロード
関数を多重定義することを、関数のオーバーロードと言います。オーバーロードで関数を定義するときに注意することは、オーバーロードで定義した全ての関数の型を満たすような関数を最後に定義することです。
class Brooch { pentagram = 'Silver Crystal'; } type Compact = { silverCrystal: boolean; } class CosmicCompact implements Compact { silverCrystal = true; cosmicPower = true; } class CrisisCompact implements Compact { silverCrystal = true; moonChalice = true; } function transform(): void; function transform(item: Brooch): void; function transform(item: Compact): void; // 上のオーバーロードした関数の型を満たすような、 // 関数を定義する。 function transform(item?: Brooch | Compact): void { if (item instanceof Brooch) { console.log('Moon crystal power, make up!'); } else if (item instanceof CosmicCompact) { console.log('Moon cosmic power, makeup!'); } else if (item instanceof CrisisCompact) { console.log('Moon crisis, makeup!'); } else if (!item) { console.log('Moon prisim power, makeup!'); } else { console.log('Item is fake'); } } transform(); transform(new Brooch()); transform(new CosmicCompact()); transform(new CrisisCompact());
- 引数の型の指定はクラスだけではなく、
implements
元のオブジェクトの型(インターフェース)でも指定できます。 implements
元のConpact
を指定する事で、CosmicCompact
とCrisisCompact
を指定できています。
オーバーロードの書き方での気づき
- 先に定義した関数を消して実際に実行する最後の関数のみでも実行できました。
- 先に定義した関数で型チェックができているので実行する関数の引数は(item?: any)でも実行できます。
ポケモンを例にしてオーバーロードの例を書いてみました。
class Mizu { Ebui = 'Shawa-zu'; } class Kaminari { Ebui = 'Sanda-su' } class Honoo { Ebui = 'Bu-suta-' } type Transform = { (): void; (item: Mizu): void; (item: Kaminari): void; (item: Honoo): void; } const transform: Transform = (item?) => { if (item instanceof Mizu) { console.log('シャワーズに進化した!!'); } else if (item instanceof Kaminari) { console.log('サンダーズに進化した!!'); } else if (item instanceof Honoo) { console.log('ブースターに進化した!!'); } else if (!item) { console.log('イーブイやないかい'); } else { console.log('にせものやないかい'); } } transform(); // "イーブイのままやないかい" transform(new Mizu()); // "シャワーズに進化した!!" transform(new Kaminari()); // "サンダースに進化した!!" transform(new Honoo()); // "ブースターに進化した!!" transform({ Ebui: 'Shawa-zu' });
出力結果
▶"イーブイのままやないかい"
▶"シャワーズに進化した!!"
▶"サンダースに進化した!!"
▶"ブースターに進化した!!"
▶"にせものやないかい"
- 5つ目の関数の実行の例から、クラスのインスタンスではないものを渡しても構造が同じ型を渡すとTypeScript上では型チェックは通ることがわかります。
(TypeScriptの型の互換性の判定が、その名前ではなく構造的サブタイピングによって行われているためです。) - JavaScriptのinstance ofでは真にならないため型チェックだけではなくインスタンスのチェックの処理を書くことで堅牢性のあるコードにしています。また、そうすべきです。
構造的サブタイピング(構造的部分型)をひとことで言うと
- インターフェースやクラスの型ではなくそのオブジェクトが持っているプロパティの型が等しいかで型チェックが行われる仕組みです。