型ガードというワードは知っていても、どんな時に使うのか?など理解が曖昧だったので調べ直しました。
TypeScriptを理解するための助けになれば幸いです。
型ガードとは?
ある値に対して特定の型であるかチェックし、コードの特定の部分で変数が特定の型を持っていると保証することです。
型を判別する方法
型を判別する方法はいくつかあります。
- typeof演算子
- instanceof演算子
- in演算子
これらを利用してユーザー定義型の型ガードを定義します!
型ガードの方法
本題です。
特定の型であることを保証する方法はいろいろあります。
typeof
演算子
typeof
はプリミティブ型と呼ばれる基本的な型を判定する際に利用します。
function hoge(value: string | number) {
if (typeof value === 'string') {
console.log('string')
} else {
console.log('number')
}
}
null
の場合object
を返すのでその点は注意が必要です…
instanceof
演算子
値がクラスオブジェクトのインスタンスであるかどうかを判別して、真偽値を返します。
class Foo {
foo = 123;
common = '123';
}
class Bar {
bar = 123;
common = '123';
}
function doStuff(arg: Foo | Bar) {
if (arg instanceof Foo) {
console.log(arg.foo); // OK
console.log(arg.bar); // Error!
}
if (arg instanceof Bar) {
console.log(arg.foo); // Error!
console.log(arg.bar); // OK
}
console.log(arg.common); // OK
console.log(arg.foo); // Error!
console.log(arg.bar); // Error!
}
doStuff(new Foo());
doStuff(new Bar());
in
演算子
オブジェクトが特定のプロパティを持つかを判定し、真偽値を返します。
interface Wizard {
castMagic(): void;
}
interface SwordMan {
slashSword(): void;
}
function attack(player: Wizard | SwordMan) {
if ("castMagic" in player) {
player.castMagic();
} else {
player.slashSword();
}
}
少し話がずれますが、strictNullChecks1を有効にしていると== null
or != null
チェックをすることでnull
とundefined
の両方を排除できます!
ユーザー定義の型ガード関数
ユーザー定義の型ガード関数を作るためには型述語(Type Predicate)を利用します。
Type predicateの宣言は戻り値がboolean型の関数に対して適用できます。
function isDuck(animal: Animal): animal is Duck {
return animal instanceof Duck;
}
is
演算子を使用して、返り値の型を引数名 is 型
とします。そして、関数の返り値がtrueの場合、引数名に与えられた値が型に絞り込みます。
関数の返り値の型を動的に制御し、より柔軟な型チェックができます。
型述語の利点
- 型ガードと組み合わせてより複雑な条件を満たすかどうかをチェックできる
- 関数の返り値の型を制御し、型エラーを早期発見できコードの安全性を高められる
ユースケースとして、配列からnullやundefinedの型情報を削除する時にユーザー定義型ガードを利用できるとのこと。
const array = [
"Apple",
null,
"Orange",
undefined,
"Banana"
]
const filtedArray = array.filter((val) => val != null);
// 型:const filtedArray: (string | null | undefined)[]
上記を下記のように変えると型からnull
とundefined
を排除できます!
const filtedArray = array.filter((val): val is string => val != null);
// 型:const filtedArray: string[]
ただ、TypeScript5.5からはType Predicate
を利用しなくても型をstring[]
だと推論してくれます。詳しくは公式のアップデート情報を確認ください!
また、TypeScript5.5のアップデート内容をざっくり把握したい場合は別記事にまとめたのでよろしければご一読ください!
NonNullable
ユーティリティ型のNonNullable<T>
を利用することでnullとundefinedを排除できます。
type T0 = NonNullable<string | number | undefined>;
// type T0 = string | number
型ガードの結果を変数に代入できる
TypeScript4.4以降では以下のように型ガードの結果を変数に代入して再利用可能になりました。
function foo(arg: unknown) {
+ const argIsString = typeof arg === "string";
- if (typeof arg === "string") {
+ if (argIsString) {
// We know 'arg' is a string now.
console.log(arg.toUpperCase());
}
}
まとめ
型安全な状態を保つ方法は色々あるのだと改めて勉強になりました。
現時点で全てを使いこなせる自信はありませんが、こんな方法があるのだと知っているだけでも違うと思うので調べ直して良かったと思います!
参考記事
-
strictNullChecksはnullやundefinedのチェックを厳しくするコンパイラオプションです。 ↩