【TypeScript】型ガードと型アサーションでunknown型を使い勝手良くする

はじめに

TypeScript3.0から登場のunknown型。
any型から進化し、型安全は保証されていますが、
型を特定する処理を加えないと、コンパイルを通してくれません。

そこで今回はunknown型と併用して型を特定してあげる役割を持っている「型アサーション」と「型ガード」についてみていきます。

ちなみにunknownとの抱き合わせの本命は「型ガード」で、「型アサーション」はオマケ程度!って認識でOKです。

型アサーション(as)について

型アサーションとは、型定義の上書き機能です。
「アサーション=断定」の意味通り、型推論を押しのけて、型定義を上書きできるので、unknown型を上書きしてコンパイルを通すのですね。

書き方はasの後に上書きしたい型を書きます。

type Foo {
    bar: number;
    bas: string;
}

const foo = {} as Foo;
foo.bar = 123;
foo.bas = 'hello';

普通ならfooは空のオブジェクトとして生成されているので、foo.bar、foo.basは存在せずコンパイルエラーになります。

しかし、型アサーションでFoo型を上書きしているので、エラーにならない訳です。

型アサーションの危険性

大前提として、型アサーションはあまり使わない方が良いです!!!

型アサーションを使えば、任意のタイミングで型を上書きできる →型安全性が全く保証されない

となるからです。

型アサーションの使い所

unknown型との抱き合わせは、基本的に型ガードを使う事が推奨されています。

型アサーションの使い道として、多くのコンパイルエラーをとりあえず消したい時などが有効です。
例えばJsなど型定義がないコードから、Tsへの移行時などで、型がまだちゃんと敷かれていない時などの応急処置に使うイメージですね。

型ガードについて

型ガードを使う事で、ブロック内のオブジェクトの型を制限出来ます。
そうすることで、unknown型のままではコンパイルエラーになっていた処理を安全に通す事ができます。

typeofでプリミティブ型を扱う

まず、プリミティブ型の型ガードにはtypeofを用います。

function doSomething(x: unknown) {
    if (typeof x === 'string') {

         //型ガードにより、xがstring型と判断されている為、substr()が使える
        console.log(x.substr(1)); 
    }
    //unknownのままだと、string型とは特定できないのでエラーになる
    x.substr(1);
}

instance of でクラスインスタンスを扱う

クラスの場合はinstanceofを使います。
使い方はtypeofの時と、ほとんど一緒です。

class Base { common = 'common'; }
class Foo extends Base { foo = () => { console.log('foo'); } }
class Bar extends Base { bar = () => { console.log('bar'); } }

const doDivide = (arg: Foo | Bar) => {

  if (arg instanceof Foo) {
    arg.foo();  
    arg.bar(); //Fooの場合は、barはないのでエラー

   //instanceofはeif文の様にlseを使うことも可能
  } else {
    arg.bar();  
    arg.foo(); //Barの場合は、fooはないのでエラー
  }
  console.log(arg.common);
};
doDivide(new Foo());
doDivide(new Bar());

ユーザー定義の型ガード(is)

プリミティブ型やクラスインスタンス型と違い、オブジェクト型などに対応する型ガードはTypeScriptには用意がありません。そこで自分たちで型ガードを作る必要が出てきます。

「is」は型ガードの条件式を切り出す事ができ、ユーザー定義の型ガードと呼ばれます。

関数の返り値を引数is Tと定義すると、
「その関数がtrueを返す場合は引数はTであり、falseを返す場合はTではない」という型ガードの条件式になります。

const bar = (arg: number | string) => {
  const isNumber = (arg: number | string): arg is number =>

    //戻り値でtrueを返しているので、argはnumber型になる
    typeof arg === "number";
};

ユーザー定義の型ガード(is)も、コンパイラに型を開発者が押し付ける事ができるという点では、
型アサーション(as)と危険度があまり変わらない為、実行時のエラーが出ない様に漏れを無くしましょう。

まとめ

any型からの反動で、とりあえず全てをコンパイルエラーしているレベルのunknown型ですが、
型アサーションと型ガードを使いこなす事で、実際に使い勝手よく書く事ができます。

最後まで読んでいただきありがとうございます。
Twitterもやっているので、興味あればご覧になってください!

参考文献

りあクト! TypeScriptで始めるつらくないReact開発 第3版【Ⅰ. 言語・環境編】
 - 4章 TypeScriptで型をご安全に
TypeScript の型ガードの注意点と解決法
unknown型とタイプガードと私
TypeScriptの as って何です?(型アサーションについて)

TypeScriptの入門記事一覧

【TypeScript入門】JavaScriptとの違いや特徴を詳しく解説
【TypeScript入門】基本的な型の定義方法
【TypeScript入門】関数とクラスの型定義(継承と合成)
【TypeScript入門】クラスの型定義(インターフェースと型エイリアスの違い)
【TypeScript】高度な型表現について
【TypeScript】型ガードと型アサーションでunknown型を使い勝手良くする

Select the fields to be shown. Others will be hidden. Drag and drop to rearrange the order.
  • Image
  • SKU
  • Rating
  • Price
  • Stock
  • Availability
  • Add to cart
  • Description
  • Content
  • Weight
  • Dimensions
  • Additional information
  • Attributes
  • Custom attributes
  • Custom fields
Compare
Wishlist 0
Open wishlist page Continue shopping