TypeScriptって良いですよね。

プログラミングは趣味オンリーですが、作るプログラムの規模が大きくなればなるほど、仕様変更の際などにちゃんと型を整備することの大切さを思い知らされます。

そんな中で、「as」「is」って何?見慣れないな、と思ったので、自分の理解を深めるためにもまとめます。

3行で

  1. 「as」 = Type Assertion
  2. 「is」 ≒ Type Guard
  3. 両者は全然違う

使い方

as

  interface Hoge {x:number}
  interface Fuga {y:number}
  
  const getHogeFuga = (input:"hoge"|"fuga"):Hoge|Fuga=>{
    const hogehoge:Hoge = {x:0};
    const fugafuga:Fuga = {y:0};
    return input === "hoge" ? hogehoge : fugafuga;
  }

  const exec = ()=>{
    let HOGE:Hoge = getHogeFuga("hoge") as Hoge; //ココ!
    console.log(HOGE.x);
    ...
  }

上記コードにおいて、getHogeFugaは引数によってHoge型またはFuga型のどちらかを返します。

getHogeFuga(“hoge”)とした場合、返ってくる値はhogehogeで、その型はHogeです。

exec()において、HOGEはHoge型なので、このままではコンパイルエラーが表示されます。

Type ‘Hoge | Fuga’ is not assignable to type ‘Hoge’.

ここで、上記コードのようにas Hogeと書くことで、あくまでHoge型として認識させることが出来る、ということです。

ただ、ご想像のとおり、実際にはgetHogeFuga()からFuga型のデータが渡されることもある、ということですので、安全ではありません。

(ドキュメントでも、「アサーションは害」とまで言われています。かわいそう)

あくまで型チェックを誤魔化しているにすぎない、って感じですかね?

is

const isNumber = (input:any):input is number=>typeof input === "number";

一言で表すと、「input is number」の場合、「inputがnumber型の場合trueを返す」、ということを意味しています。

関数の中身で、typeof演算子でnumber型かどうか評価していることからも、感覚的に理解いただけると思います。

const isNumber = (input:any):boolean=>typeof input === "number";

↑こうでもいいんじゃないの?と思いましたか?僕は思いました。

「User Defined Type Guard」とも称される通り、inputがnumber型である場合trueを返すという点のほか、inputがnumber型であることを確定する点で有用です。

const isNumber = (input:any):input is number=>typeof input === "number";
      
const fun = (input:string|number):string=>{
  if(isNumber(input)){
    //inputはnumber型であることが確定
      
    return input;
    //Type 'number' is not assignable to type 'string'.
  }else{
    //inputはnumber型じゃないことが確定
      
    return input.toFixed(2);
    //Property 'toFixed' does not exist on type 'string'.
  }
}

fun("1.00");
fun(1);

上のような、if構文内の返り値が逆になっているコードがあったとしましょう。

toFixed()はNumberクラスオブジェクトにあってStringクラスオブジェクトにないメソッドです。

また、fun()関数はstring型を返すと指定しました。

したがって、順当に上のように2つ、エラーが表示されます。

まとめ

「as」は逃げの一手、「is」は攻めの一手(?????)