こんにちは、フロントエンドエンジニアのまさにょんです。
今回は、TypeScriptでの「as」(Type-Assertion)について解説していきます。
目次
「as」とは何か?なぜ「as」なのか?
TypeScriptだとでてくる「 as 」とは何なのでしょうか?
迷えるTypeScript使いの指南書『サバイバルTypeScript』から引用します。
TypeScriptには、型推論を上書きする機能があります。
その機能を型アサーション(type assertion)と言います。
TypeScriptコンパイラーはコードをヒントに型を推論してくれます。
その型推論は非常に知的ですが、場合によってはコンパイラーよりもプログラマーがより正確な型を知っている場合があります。
そのような場合は、型アサーションを用いるとコンパイラーに型を伝えることができます。
型アサーションはコンパイラに「私を信じて!私のほうが型に詳しいから」と伝えるようなものです。
引用元: 型アサーション「as」(type assertion) – サバイバルTypeScript
上記に明記されているように、
「 as 」は「型アサーション」(Type-Assertion) と言うTypeScriptの機能であり、
「型推論」された変数の「型」を宣言的・明示的に「上書きする」ための記法です。
ちなみに「Assertion」は、 主張, 断言, 明言 と言った意味の英単語です。
英語のType-Assertionを直訳すると「型の明言」と言ったところなので、「型」宣言的・明示的に「上書きする」ための機能であることをそのまま表してくれています。
次に、なぜ「 as 」なのか?
これは「 Assertion 」の「 As 」と英単語の「 as 」、両方をかけていると思われます。
英語の「 as 」は「 = 」のニュアンス(Nuance)を持っています。
なので英語ネイティブの感覚からすると「 as 」を使って「型」を宣言するのは、宣言的・明示的に開発者が「 = 」で「型」を後から結びつけている(上書きする)ようなものだと思います。
「 as 」(Type-Assertion)は、どんなタイミングで必要なのか?
「 as 」の意味や、なぜ「 as 」なのか理解したところで、実際のところ「 as 」は、どんなタイミングで必要になってくるのか気になりませんか?
「 as 」が必要になるタイミングは、決まっています。
それはTypeScriptのCompilerによる「型推論」が裏目にでるケースです。
それではCode上で、「型推論」が裏目にでるケースを見ていきましょう!
// < Error-Pattern >
// 1. TypeScriptのCompilerが「 {} 」(空-Object)と判定をする!
const robo = {};
// 2. 「key & value」を追加しようとすると「型-Error」が発生する!
robo.robotama = 'ロボ玉';
上記のようなCompilerによる「型推論」(初期の型定義)から上書きしたい場合に「 as 」を使います!
具体的には、次のように記述します。
// < Success-Pattern >
// 1. 事前にkey & value を定義する => Data-Fetchなどで後から追加される値を定義する!
interface Robotama {
robotama: string;
}
// 2. 「 as 」を使って「型推論」を上書きする!
const robo = {} as Robotama;
// 3. Success!
robo.robotama = 'ロボ玉';
上記の処理を日本語でまとめると、次のようになります。
1. TypeScriptのCompilerが「 {} 」(空-Object)と判定をする => 「型推論」であり「初期の型定義」
2. しかし、このObjectには、後からDataが追加される予定である。
3. そこで、Data-Fetchなどで後から追加されるであろう値を定義した「型」を用意する。
4. 「 as 」で「型推論」を上書きする!
5. 「 as 」上書きされた「宣言的・明示的な型」によって、Success!
ダブル・アサーション(Double-Assertion)
「 as 」は、二重がけすることができます。
「any」や「unknown」を通すことで、型を自由にAssertionできます。
ただ、この技はErrorの温床に可能性が高いので、基本的に使用しない方がいいと思います。
TypeScriptで型-Checkしている意味がなくなるわけですから・・・
ただし、場合によっては有効な場合もあります。
// < Double-Assertion >
const numValue = 123;
// 1.「any」を通した「Double-Assertion」で型-Check-Through!
const strValue = numValue as any as string;
// 2.「unknown」を通した「Double-Assertion」で型-Check-Through!
const booleanValue = numValue as unknown as boolean;
// 3. 基本的にErrorの温床になるので使用しない!
// TypeScriptを使用する意味がなくなる・・・
// 4. 場合によっては、有効な場合も、、、
// 5. anyを間に挟むことで「Event」を「HTMLElement」として扱える!
const eventHandler = (event: Event) => {
let element = event as any as HTMLElement;
}
型-アサーションとキャストは違います!
Type-Assertion(型-アサーション)とCast(キャスト)は違います!
型アサーションは、他の言語のキャストに似ています。
キャストとは、実行時にある値の型を別の型に変換することです。
型アサーションは、実行時に影響しません。
値の型変換はしないのです。
あくまでコンパイル時にコンパイラーに型を伝えるだけです。
コンパイラーはその情報を手がかりに、コードをチェックします。
型アサーションはキャストではないため、TypeScriptでは型アサーションをキャストとは呼ばないことになっています。
実行時に型変換をするには、そのためのロジックを書く必要があります。
引用元: 型アサーション「as」(type assertion) – サバイバルTypeScript
今までの説明や、上記の引用に記述のあるとおり、
Type-Assertionは「型の宣言的・明示的な上書き」であり、Castは「実行時の型を変換する」ことです。
// < Cast(型変換) >
// 1. Number() でCast(型変換)している!
const num: number = Number('123');
console.log(typeof num); // number
console.log({num}); // {num: 123}
型アサーションと型アノテーションは違います!
Type-Assertion(型-アサーション)とType-Annotation(型-アノテーション)は名前が似ていますが、違います!
型アサーションと型アノテーション(type annotation)は名前が似ているためかしばしば混同されます。
本書では型アノテーションを「型注釈」と表記しています。
この2つはTypeScriptの異なる機能です。
型注釈は、コンパイラーに「この変数に代入できるのはこの型だよ」と伝えるものです。
コンパイラーは型注釈をヒントに、その型に値が代入可能かどうかをチェックし、代入できないことが分かり次第報告してきます。
一方、型アサーションはコンパイラーに「君はこの型だと思ってるかもしれないけど、本当はこの型だよ」と型推論の不正確さを伝えるものです。
引用元: 型アサーション「as」(type assertion) – サバイバルTypeScript
今までの説明や、上記の引用に記述のあるとおり、
Type-Assertionは「型の宣言的・明示的な上書き」であり、Type-Annotationは「型を伝える」ことです。
// < 型推論-型注釈-TypeAssertion-比較 >
// 1. 型推論: Compilerは、stringと型推論する!
const robotamaString = 'ロボ玉';
// 2. 型注釈: あらかじめ「型」を定義する => 型に注釈(説明)を付けて、Compilerに伝える!
let robotamaFlag: boolean;
// 3. TypeAssertion: 型推論で「 never[] 」と推測されていたが「 as 」で上書きする!
const robotamaStrArray = [] as string[] ;
robotamaStrArray.push('ロボ玉');
ちなみに「Annotation」は、注釈, 注記 という意味の英単語です。
まとめ
- 「 as 」は「型アサーション」(Type-Assertion) と言うTypeScriptの機能
- 「 as 」を使うと「型推論」された変数の「型」を宣言的・明示的に「上書きする」ことができる
- 英語の「 as 」は「 = 」のニュアンス(Nuance)を持っている
- 「 as 」が必要になるタイミングは、TypeScriptのCompilerによる「型推論」が裏目にでる時
- 「型推論」という「初期の型定義」を上書きしたい場合に「 as 」を使用する