34
27

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 5 years have passed since last update.

[TypeScript]Objectのプロパティチェックと自動型生成

Last updated at Posted at 2019-11-04

※201911/06追記
npmに今回の内容をパッケージ化したhasproperty-tsを登録しました

[TypeScript]Objectのプロパティチェックと自動型生成

※同じ記事をこちらにも書いています

 TypeScriptでオブジェクトにプロパティが存在するかどうかを確認する場合には、inキーワードが使用できます。しかしinでメンバを判別しても前提の型情報が存在しないと、判別したプロパティを使用することが出来ません。これを解決する方法を紹介します。

1.エラーが出る例

function Test01(x:unknown){
  if(x instanceof Object && "foo" in x){
    console.log(x.foo); //Error
    console.log((<{foo:unknown}>x).foo); //OKだけど不便
  }
}

 xの型情報をunknownにした状態でfooがプロパティとして判定するためには、まずxのインスタンスがObjectかどうか判別する必要があります。Objectならばinキーワードを利用して、fooプロパティがあるかどうかを確認していきます。

 しかしメンバにxがあることが分かっても、ifブロックの中でのxの型はneverとされてしまい、fooを使用するとエラーとなってしまいます。fooがあると分かっていても、いったんオブジェクト側をキャストしないとメンバを使用することは出来ません。これはひじょうに不便な状態です。

2.そもそものinの用途

 そもそもTypeScriptでinを有効に使うためには、事前に前提となる型情報を与えておく必要があります。unionのように複数の候補の中から目的の型を選べるようにしておかなければなりません。

function Test02(x: { foo1: unknown,bar1:unknown } | { foo2: unknown,bar2:unknown }) {
  if ("foo1" in x) {
    console.log(x.foo1); //OK
    console.log(x.bar1); //OK
    console.log(x.foo2); //Error
    console.log(x.bar2); //Error
  }
}

3.プロパティチェックを使いやすくする

 このままでは面倒なので、プロパティのチェックと型の生成を同時に行うfunctionを作ってみます。

function hasProperty<K extends string>(
  x: unknown,
  name: K
): x is { [M in K]: unknown } {
  return x instanceof Object && name in x;
}

function Test03(x: unknown) {
  if (hasProperty(x, "foo")) {
    console.log(x.foo); //キャスト無しでFooが使える
  }
}

 hasPropertyが真を返すと、判定したプロパティを持つオブジェクトとして型が決定します。これで毎回手動でオブジェクト側の型をキャストする必要がなくなります。

4.複数同時にチェックできるようにする

 プロパティをまとめてチェック出来るように、nameを可変引数にします

function hasProperty2<K extends string>(
  x: unknown,
  ...name: K[]
): x is { [M in K]: unknown } {
  return (
    x instanceof Object && name.every(prop => prop in x)
  );
}

function Test04(x: unknown) {
  if (hasProperty2(x, "foo", "bar")) {
    console.log(x.foo); //Fooが使える
    console.log(x.bar); //Barが使える
  }
}

 これでオブジェクト側に余計なキャストをイチイチ書かなくとも、直接プロパティを扱えるようになりました。

5.まとめ

 プロパティの存在チェックと型の生成は標準でサポートされていても良さそうな気がしますが、無いなら自分で作れるのがTypeScriptの良いところです。

34
27
2

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
34
27

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?