はじめに
今回はちょっとした面白企画です。 クイズ形式でTypeScriptの型への理解度をチェックしてみましょう!
クイズ
以下クイズです。問題がコードで書かれています。 答えはアコーディオンで閉じていますので開いて確認してください。
なお、以下で書かれている「エラー」はコンパイルエラーを指します。実行時エラーではありません。
Q1
const q1: string = 1; // これはエラーになる?
答え
Type 'number' is not assignable to type 'string'.
stringにnumberは入れられないのでエラーです。直感的にもわかるレベルですね。
Q2
const q2: any = 1; // これはエラーになる?
答え
エラーにはなりません。anyはnumberを入れることができます。
Q3
const q3: number = null; // これはエラーになる?
答え
Type 'null' is not assignable to type 'number'.
プログラムの概念的にはnullableとかいう言葉もありますし、nullって特別扱いされていそうですがTypeScriptにおいては普通に型の一種なんですね。なのでnumber型はnull型ではないのでエラーになります。
Q4
const q4: number | null = undefined; // これはエラーになる?
答え
Type 'undefined' is not assignable to type 'number | null'.
undefinedってなんなんでしょうね。nullは存在しないってことだし、undefinedも存在しなさそうだから同じもの?違うんですね。undefinedも専用の型が存在します。
Q5
const q5_1: any = null; // これはエラーになる? const q5_2: any = undefined; // これはエラーになる?
答え
エラーになりません。anyはnullもundefinedも入れられます。
Q6
const q6_1: unknown = "hoge"; // これはエラーになる? const q6_2: unknown = 1; // これはエラーになる?
答え
エラーになりません。unknownにはstringもnumberも入れられます。
Q7
const q7_1: unknown = "fuga"; const q7_2: string = q7_1; // これはエラーになる?
答え
Type 'unknown' is not assignable to type 'string'.
q7_1に代入している値は文字列なので文字列をstringには入れられそうですがエラーです。unknownに代入した時点でそれが本当はstringかどうかは隠蔽されてしまうからです。
Q8
const q8_1: string = "piyo"; const q8_2: never = q8_1; // これはエラーになる?
答え
Type 'string' is not assignable to type 'never'.
見慣れない型のneverが出てきました。neverはanyやunknownと真逆でどんな値も代入できません。
Q9
type Hoge = { id: number; name: string; }; type Fuga = { id: number; name: string; }; const q9_1: Hoge = { id: 1, name: "hoge", }; const q9_2: Fuga = q9_1; // これはエラーになる?
答え
エラーになりません。Fuga型にHoge型を入れられなさそうですがtypeにつけたHogeやFugaはただのエイリアスでしかないので、中身は同じオブジェクトの定義です。なので実はこの2つのtypeはts上は同一の型になります。
Q10
type Hoge = { id: number; name: string; }; type Piyo = { id: number; name: string; age: number; } const q10_1: Hoge = { id: 1, name: "hoge", }; const q10_2: Piyo = { id: 1, name: "hoge", age: 10, }; const q10_3: Hoge = q10_2; // これはエラーになる? const q10_4: Piyo = q10_1; // これはエラーになる?
答え
Property 'age' is missing in type 'Hoge' but required in type 'Piyo'.
前者はエラーになりませんが後者はエラーになります。
Q11
type Hoge = { id: number; name: string; }; const q11: Hoge = { id: 1, name: "hoge", age: 10, }; // これはエラーになる?
答え
Object literal may only specify known properties, and 'age' does not exist in type 'Hoge'.
Q10でHogeにPiyoを代入できたんだからなんだかいけそうなものですがダメなんですね。変数から変数への代入時は不要なプロパティがあっても許されますが、リテラルに代入しようとすると不要なプロパティは許されません。難しいですね。
Q12
type Toto = { id: number; name?: string; }; const q12: Toto = { id: 1, name: null, }; // これはエラーになる?
答え
Type 'null' is not assignable to type 'string | undefined'.
オブジェクトの?
記法は型定義にundefinedを追加するものです。Q4でやった通り、undefinedとnullは別物なので入れられません。
解説
さて、何問正解できましたか? なんとなく正解してしまった方のためにも以下簡単に解説を付属しておきます。
集合でとらえる
unknownにstringが代入できる等の概念は集合で理解するとわかりやすいです。
以下の記事がとても参考になりますのでオススメです。
unknownとanyの違い
端的に言うなら、unknownはまだ型が判明していないだけ、anyは型チェックを放棄した型です。
anyは使うなというのは聞いたことがあると思いますが、unknownを使うなというのは聞かないですよね。 そもそもTypeScriptはJavaScriptに型の概念を付与したものですから、型チェックの放棄はJSに戻っていてTSの意味がありません。だから「anyは使うな」なのです。
それに対し、unknownは単にその時点で型が判明していないだけなので、その後の処理で
const hoge: unknown = fuga; // fugaはこれ以前の処理で得られたもので型が不明 if (typeof hoge === "string") { hoge.startsWith("hoge") }
上記のようにtypeof
で正しく分岐することで型安全が保たれたままにすることができます。
以下で詳しく解説されています。
neverについて
多くの方が使ったことがないと思われる型です。
値を持たない変数なんて使い道がなさそうだし、値をreturnしない関数ならvoid型だよね?何に使うの?
以下のコードを見てください。
const getVoid = (): void => { console.log("void"); }; const v = getVoid(); console.log(v); // これはundefinedとなる
void型の関数の戻り値を受け取った場合、それはundefinedとして扱われます。 もう少し噛み砕くと、何も返さない処理は内部的にはundefinedを返していることになります。 つまりvoid型の関数はundefinedを返しているということになります。
次に以下のコードを見てください。
const getNever = (): never => { console.log("void"); }; // この時点で定義できずにエラーになる
never型はundefinedすら入れられないので、undefinedを返すことも許されません。 そのため上記は定義できません。
じゃあ結局どう使うのかですが、
const getNever = (): never => { console.log("never"); throw Error("never"); };
これならエラーになりません。つまり、100%エラーになる処理などで使うことができます。 もう少し実用的な例だと、
const generateUser = (): never => { // TODO: ここにユーザー生成のロジックを実装する throw Error("not implemented"); };
このように未実装の関数を定義する場合などにそれを絶対に呼び出せない形で形式上置いておくことができます。 私もこれくらいしか今まで使えたことがありません。
先程から何度もリンクさせていただいているサバイバルTypeScript様にも解説がありますのでもっと詳しくなりたい方はそちらをお読みください。
Playgroundリンク
現在、ミツカリではITエンジニアを募集しています。興味のある方はぜひお気軽にご連絡ください!