2022年4月9日 りあクト! 第4章 型アサーションと型ガード asによる型アサーション (p.203~)
復習
in演算子は、指定したオブジェクト上に指定したプロパティがあるかを判定できます。
"プロパティ名" in オブジェクト; // true or false
型アサーション
型を断定するものです。as 断定する型
とします。
コンパイラによる型の解釈を変えているだけで、実際の値が変化しているわけではありません。
問題点
以下のケースでは、無理やりコンパイラが通っており、実行時にエラーになっています。つまり、型の安全性が保証されていません。
type User = { username: string; address: { zipcode: string; town: string } }; const str:unknown = { username: "patty", town: "Maple Town" }; const user = str as User; console.log(user.address.town); // TypeError: Cannot read property 'town' of undefined
型アサーションはスーパータイプ、サブタイプの関係性がないと使用することが出来ません。
型ガードを使うことで、任意の型に絞って型安全性を保ちながら型を割り出すことができます。
型ガード
- 型ガードの種類
型アサーションと型ガードの違い
中身の型がわからないものの型を断定する事で、その値のプロパティやメソッドにアクセスできるが、型定義にあって実際のプロパティやメソッドにないものにアクセスするとコンパイルエラーにならずに実行時にエラーになるため型安全が保証されていません。
type User = { username: string; address: {zipcode: string; town: string} }; const str = `{"username": "patty", "town": "Maple Town"}`; const data: unknown = JSON.parse(str); const user = data as User; // TypeScriptのコンパイルエラーが出ない。 // TypeScriptのコンパイル時にはエラーが出ず、実行時にエラーが出る。 console.log(user.address.town); // => Cannot read properties of undefined (reading 'town')
型ガード
中身の型がわからないものをif文で型を絞り込むため、型安全が保証された上でプロパティにアクセスできます。 以下はtypeofを使った例です。
const foo: unknown = '1,2,3,4'; if (typeof foo === 'string') { console.log(foo.split(',')); } consolo.log(foo.split(',')); // compile error!
- typeof で型ガードを行なっています。
- typeofによってstring型だと判断されたブロック内では、変数fooに stringのプロトタイプメソッドである split()が使えています。
本章
カリー化
カリー化の説明は「りあクト! 【Ⅰ. 言語・環境編】p.136」に記述されている為、以下に引用します。
複数の引数を取る関数を、引数が「元の関数の最初の引数」で戻り値が「引数として元の関数の残りの引数を取り、それを使って結果を返す関数」である高階関数にすることを「カリー化」と呼ぶ。
// JavaScript const add = a => b => a + b; // TypeScript // TypeScriptでは、関数の引数に型アノテーションをつける必要がある const add = (a: number) => (b: number) => a + b; console.log(add(1)(2)); // 3
型ガード 応用
type Result<T, E extends Error> = Ok<T, E> | Err<T, E>; export class Ok<T, E extends Error> { constructor(readonly val: T) {} isOk = (): this is Ok<T, E> => true; isErr = (): this is Err<T, E> => false; } export class Err<T, E extends Error> { constructor(readonly err: E) {} isOk = (): this is Ok<T, E> => false; isErr = (): this is Err<T, E> => true; } export const withResult = <T, A extends any[], E extends Error>( fn: (...args: A) => Promise<T>, ) => async (...args: A): Promise<Result<T, E>> => { try { return new Ok(await fn(...args)); } catch (error) { if (error instanceof Error) { return new Err(error as E); } } };
- このコードに関しては全体を読み解きながら復習のための議論をしましたが、ブログにアウトプットする内容にはならなかったため、コードの掲載のみにとどめます。