https://qiita.com/ryo2132/items/ce9e13899e45dcfaff9b
TypeScript の構文の中でも、屈指のググラビリティの低さを誇る is
と in
を自分なりにまとめてみました。
is
は TypeScript の型推論を補強する user-defined type guard(ユーザー定義型ガード)
で使われます。
unknown 型や、any 型、Union 型の型の絞り込みを行えます。
例えば、unknown 型を引数に受け取る関数で、もし引数の型が string 型だったら文字列の長さを出力するという処理があるとします。 その場合は、以下のように型を絞り込む必要があります。
const example = (foo: unknown) => {
if (typeof foo === "string") {
console.log(foo.length); // fooはstringとして推論される
}
};
この一つの関数ならば良いのですが、他にも string 型への絞り込みが必要な関数があり、typeof foo === "string"
の部分を isString
関数に抽出して汎用的に使いまわせるようにしたいとします。
一見上手くいきそうですが、typeof での型の絞り込みは関数スコープで完結してしまうので、isString
が true な場合でも、まだ foo は unknown 型として推論され型の絞り込みが行えません。
const isString = (test: unknown): boolean => {
return typeof test === "string";
};
const example = (foo: unknown) => {
if (isString(foo)) {
console.log(foo.length); // Error fooはまだunknownとして推論される
}
};
このような時が is
の出番です。is
を使うと isString
の結果が true の場合は引数で受け取った変数の型は、string 型であるとコンパイラに教えることができます。
const isString = (test: unknown): test is string => {
return typeof test === "string";
};
const example = (foo: unknown) => {
if (isString(foo)) {
console.log(foo.length); // fooはstringとして推論される
}
};
また、is
はオブジェクト型の型の絞り込みにも使えます。
例えば Fish か Bard の Union 型の型をどちらかの型に絞り込みたい時はif((fishOrBard as Bard).fly() !== undefined){}
で判定を行えそうです。
しかし、この判定ではまだ TypeScript のコンパイラは fishOrBard
を Bard 型と推論してはくれません。
type Bard = {
fly: () => {
/* Do something */
};
};
type Fish = {
swim: () => {
/* Do something */
};
};
const example = (fishOrBard: Fish | Bard) => {
if ((fishOrBard as Bard).fly() !== undefined) {
console.log(fishOrBard.fly); // Fish | Bard 型として推論され Fishはfly()を持たないので type error
} else {
console.log(fishOrBard.swim); // Fish | Bard 型として推論され Bardはswim()を持たないので type error
}
};
そのような時に (fishOrBard as Bard).fly() !== undefined
を関数に切り出し、is
で型を指定するとコンパイラは型を推論できるようになります。
const isBard = (test: Fish | Bard): test is Bard => {
return (test as Bard).fly() !== undefined;
};
const example = (fishOrBard: Fish | Bard) => {
if (isBard(fishOrBard)) {
console.log(fishOrBard.fly); // Bard型として推論される
} else {
console.log(fishOrBard.swim); // Fish型として推論される
}
};
is
自体への型推論は効かないので注意です。間違った指定をしてしまうとコンパイルは通っても実行時エラーになります。
const isString = (test: unknown): test is string => {
return typeof test === "number";
};
const example = (foo: unknown) => {
if (isString(foo)) {
console.log(foo.length); // 型エラーは出ないが、fooはnumber型なので実行時Errorが発生する
}
};