※このブログではサーバー運用、技術の検証等の費用のため広告をいれています。
記事が見づらいなどの問題がありましたらContactからお知らせください。


【TypeScript】型アサーションの使用方法まとめ

Type TypeScript 型付き言語 型アサーション

投稿日:2021年2月23日

このエントリーをはてなブックマークに追加

基本

パブリックなフィールドを持つクラスの場合

まずは簡単な例からはじめましょう。

以下のようなUserというクラスと、そのクラスのオブジェクトを引数として受け取るlogInformationという関数があったとします。

/**
 * フィールドがpublicなクラス
 */
class User {
  constructor(public id: number, public name: string) {}
  getInfo() {
    return `${this.id} : ${this.name}`;
  }
}

/**
 * Userの情報をログに出力する関数
 * @param user 
 */
function logInformation(user: User) {
  console.log(`${user.id} : ${user.name}`);
}

TypeScriptの場合、オブジェクトのshapeが仮にUserクラスと同じであっても、そのオブジェクトをそのままUserとしてあつかうことはできません。

トランスパイルエラーになる
const user = { id: 1, name: "marsquai" };

logInformation(user); // [ERROR] Argument of type '{ id: number; name: string; }' is not assignable to parameter of type 'User'.

このような場合、型アサーション(as)を使ってトランスパイル時のエラーを回避できます。

const user = { id: 1, name: "marsquai" } as User;

logInformation(user);  // [ OK ]

型アサーションで対象の型として扱えるオブジェクトは型アサーションする型の部分集合である必要があります。

オブジェクト⊆型アサーションの型

以下にいくつかの例をしめしておきます。

class User {
  constructor(public id: number, public name: string) {}
  getId() {
    return this.id;
  }
  getName() {
    return this.name;
  }
}

//const user = { id: 1, name: "marsquai" } as User;
const user1 = { id: 1, name: "marsquai" } as User;   // [ OK ]
const user2 = { id: 2 } as User;                     // [ OK ]
const user3 = { id: 3, age: 16 } as User;            // [ Error ]
const user4 = {} as User;                            // [ OK ]
const user5 = { getId() {} } as User;                // [ OK ]
const user6 = { getId():string {return "4"}} as User // [ ERROR ]

注意点

ここで型アサーションはキャストではないことに注意してください。
型アサーションはあくまでトランスパイル時にオブジェクトを指定された型としてみなすだけであって、オブジェクト自体に何らかの変更が加えられているわけではありません。

以下のようなコードはトランスパイルは問題なく通りますが実行時にエラーになってしまいます。

const user = { id: 1, name: "marsquai" } as User;
user.getInfo();
実行時のエラー
user.getInfo();
     ^

TypeError: user.getInfo is not a function

userはトランスパイル時にはUserクラスのオブジェクトとして扱われますが、実際にはUserクラスのオブジェクトではないのです。

トランスパイルにエラーが発見できないコードは非常に危険です。

用途

型アサーションの危険性については十分説明したので、実際にどのような場合に使う可能性があるのでしょうか?

一番の用途がJavaScriptJSONなど型のないオブジェクトからTypeScriptへのデータ変換時です。

例えば、サーバーから送られるデータをパースしたい場合、JSON.parse()を使うことになると思いますが…

JSON.parse()の返り値の型はany型です。

TypeScriptでany型を使いたくないのは万人に共通する事項でしょう。

このような場合の解法として一旦型アサーションで型を指定し、必要なデータのみをとりだした後クラスのインスタンスを返す、という手法があります。

実際の実装例を見てみましょう。

// サーバーから受けったと仮定したデータ
const DATA_BODY =
  '[{"name": "marsquai", "id" : 1},{"name": "ogihara", "id" : 2},{"name": "akky", "id" : 3}]';

function getUser(id: number): User {
  const usersData = JSON.parse(DATA_BODY) as User[];            // 一旦Userの配列に型アサーション
  const userSelected = usersData.filter((d) => d.id == id);     // 必要なデータのみ取得
  if (userSelected.length > 0) {
    return new User(userSelected[0].id, userSelected[0].name);  // インスタンス化して返す
  } else {
    return new User(id, "no name");
  }
}

型アサーションしたデータについてデータの整合性についてのチェックを行っていない点に注目してください。

今回、フロントがサーバーから受け取ったデータについて処理をしているというケースを想定しています。フロントのHTTPリクエストに対してサーバーが謎なHTTPレスポンスを返すということは基本的にありえません。仮にもし、そのようなレスポンスが帰る場合には完全にバグが発生している状況と言えるのでエラーを表示しても問題ないですね。

ただし逆に、サーバーがフロントからのJSONリクエストを受け取りそれをパースする場合については、値のバリデーションやクラスのインスタンス化などをするほうが良いでしょう。

このエントリーをはてなブックマークに追加


関連記事

記事へのコメント