Movatterモバイル変換


[0]ホーム

URL:


LoginSignup
310

Go to list of users who liked

151

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

TypeScript の"is"と"in"を理解する

Last updated atPosted at 2020-04-29

TypeScript の構文の中でも、屈指のググラビリティの低さを誇るisin を自分なりにまとめてみました。

"is"

is は TypeScript の型推論を補強するuser-defined type guard(ユーザー定義型ガード) で使われます。
unknown 型や、any 型、Union 型の型の絞り込みを行えます。

使用例

例えば、unknown 型を引数に受け取る関数で、もし引数の型が string 型だったら文字列の長さを出力するという処理があるとします。
その場合は、以下のように型を絞り込む必要があります。

constexample=(foo:unknown)=>{if(typeoffoo==="string"){console.log(foo.length);// fooはstringとして推論される}};

この一つの関数ならば良いのですが、他にも string 型への絞り込みが必要な関数があり、typeof foo === "string" の部分をisString 関数に抽出して汎用的に使いまわせるようにしたいとします。

一見上手くいきそうですが、typeof での型の絞り込みは関数スコープで完結してしまうので、isString が true な場合でも、まだ foo は unknown 型として推論され型の絞り込みが行えません。

constisString=(test:unknown):boolean=>{returntypeoftest==="string";};constexample=(foo:unknown)=>{if(isString(foo)){console.log(foo.length);// Error fooはまだunknownとして推論される}};

このような時がis の出番です。
is を使うとisString の結果が true の場合は引数で受け取った変数の型は、string 型であるとコンパイラに教えることができます。

constisString=(test:unknown):testisstring=>{returntypeoftest==="string";};constexample=(foo:unknown)=>{if(isString(foo)){console.log(foo.length);// fooはstringとして推論される}};

また、is はオブジェクト型の型の絞り込みにも使えます。
例えば Fish か Bard の Union 型の型をどちらかの型に絞り込みたい時はif((fishOrBard as Bard).fly() !== undefined){}で判定を行えそうです。

しかし、この判定ではまだ TypeScript のコンパイラはfishOrBard を Bard 型と推論してはくれません。

typeBard={fly:()=>{/*  Do something */};};typeFish={swim:()=>{/*  Do something */};};constexample=(fishOrBard:Fish|Bard)=>{if((fishOrBardasBard).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 で型を指定するとコンパイラは型を推論できるようになります。

constisBard=(test:Fish|Bard):testisBard=>{return(testasBard).fly()!==undefined;};constexample=(fishOrBard:Fish|Bard)=>{if(isBard(fishOrBard)){console.log(fishOrBard.fly);// Bard型として推論される}else{console.log(fishOrBard.swim);// Fish型として推論される}};

注意点

is 自体への型推論は効かないので注意です。間違った指定をしてしまうとコンパイルは通っても実行時エラーになります。

constisString=(test:unknown):testisstring=>{returntypeoftest==="number";};constexample=(foo:unknown)=>{if(isString(foo)){console.log(foo.length);// 型エラーは出ないが、fooはnumber型なので実行時Errorが発生する}};

(その挙動の解決策としてこちらの記事で便利なライブラリが紹介されていました。)

"in"

in は コンテキストによって 2 つの意味を持つ構文です。
一つは JS にもあるオブジェクトが特定のプロパティを持つか判定するもの。型の絞り込みに使えます。
もう一つはkeyof 構文と組み合わせて mapped type の定義に使えます。

使用例

型の絞り込み

前述のis で オブジェクトの型判定に(fishOrBard as Bard).fly() !== undefined) を使いましたが、こちらはin で代替することができます

in を使えば判定処理を関数に切り出しis で type predicate を記述する必要もありません。

constexample=(fishOrBard:Fish|Bard)=>{// fishOrBardは'fly'というプロパティを持っているかどうかの判定if("fly"infishOrBard){console.log(fishOrBard.fly);// Bard型として推論される}else{console.log(fishOrBard.swim);// Fish型として推論される}};

mapped type での利用

keyof 構文と組み合わせて mapped type で新しい型を定義できます。
keyof プロパティ名の Union 型を取り出しin で反復処理するイメージですかね。

typeDog={name:string;run:()=>void;};typePartialDog={[PinkeyofDog]?:Dog[P];};// PartialDogは以下の型になる// { name?: string, run?: () => void}

終わりに

以上 TypeScript のisin 構文の説明でした。
分かってみると何かと便利に使えそうですね。
自分自信まだまだ勉強中なので誤った記述などあれば、コメントや修正リクエストで教えて頂けると嬉しいです!!

参考

310

Go to list of users who liked

151
3

Go to list of comments

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
310

Go to list of users who liked

151

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?


[8]ページ先頭

©2009-2025 Movatter.jp