TypeScriptの面接では、文法だけでなく型システムを理解しているかが問われます。以下は基礎から、TypeScriptを使う人と「なぜそう動くのか」を理解している人を分ける問題まで、回答付きの20問です。コードは公開前にTypeScriptコンパイラのstrictモードでコンパイルしており、引用しているエラーメッセージは実際のものです。
基礎
1. TypeScriptはJavaScriptに何を追加しますか?
コンパイル時にチェックされ、その後消去される静的型システムです。出力は素のJavaScriptなので、型に実行時コストはなく、実行時には存在しません。この最後の点は重要です。自分でチェックを書かない限り、実行時にtypeofで型を確認することはできません。
2. typeとinterfaceの違いは何ですか?
どちらもオブジェクトの形を記述し、多くの場合そのまま置き換え可能です。実用的な違いは次のとおりです。
| interface | type | |
|---|---|---|
| 宣言のマージ | する。2つの宣言がマージされる | しない。同名はエラーになる |
| ユニオンとプリミティブ | 表現できない | 表現できる。たとえばnumberとstringのユニオン |
| 拡張 | extends | &による交差型 |
| マップ型と条件型 | 不可 | 可能 |
interface Aを2回宣言するとフィールドがマージされます。type Bを2回宣言するとerror TS2300: Duplicate identifier 'B'になります。よくある目安としては、他の人が拡張する可能性のある公開オブジェクトの形にはinterfaceを使い、それ以外にはtypeを使います。
3. any、unknown、neverの違いは何ですか?
- anyはチェックを無効にします。何でも通ってしまい、エラーが隠れます。
- unknownは安全な版です。何でも代入できますが、使う前に絞り込む必要があります。unknownに対してx.toFixed()を呼ぶとerror TS18046: 'x' is of type 'unknown'で失敗します。
- neverは存在し得ない値の型で、たとえば常に例外を投げる関数などです。網羅性チェックに役立ちます。
パースしたJSONなど、型を自分で制御できない値にはanyよりunknownを選びましょう。
4. strictモードは何をしますか?
一連のチェックを有効にします。特に重要なのはstrictNullChecks(nullとundefinedが別々の型になる)とnoImplicitAnyです。これを有効にすると、オプショナルな値をチェックせずに使うとerror TS18048: 'x' is possibly 'undefined'になります。新規プロジェクトはstrictを有効にして始めるべきです。
5. readonlyとconstの違いは何ですか?
constは変数の再代入を防ぎます。readonlyはプロパティへの代入を防ぎます。readonlyプロパティに書き込むとerror TS2540: Cannot assign to 'n' because it is a read-only propertyになります。どちらも元のオブジェクトを深く不変にするわけではありません。
実践的な型
6. ユニオン型とは何で、どうやって絞り込みますか?
ユニオンは「これらの型のいずれか」という意味です。絞り込みにはコンパイラが理解するチェックを使います。
type Id = number | string;
function describe(id: Id): string {
if (typeof id === "string") return id.toUpperCase();
return id.toFixed(1);
}
ifの中ではidはstringです。その後のコンパイラはnumberだと認識しています。他にもinstanceof、in演算子、等価比較、カスタム型ガードが絞り込みの手段です。
7. 判別可能なユニオンとは何ですか?
各メンバーが、それらを見分けるための共有のリテラルフィールド(判別子)を持つユニオンです。
type Shape =
| { kind: "circle"; r: number }
| { kind: "sq"; s: number };
function assertNever(x: never): never {
throw new Error("unexpected " + x);
}
function area(s: Shape): number {
switch (s.kind) {
case "circle": return Math.PI * s.r ** 2;
case "sq": return s.s ** 2;
default: return assertNever(s);
}
}
後で新しい形を追加してcaseを書き忘れると、default節がコンパイルできなくなります。sがneverではなくなるからです。これが網羅性チェックです。
8. ジェネリクスとは何で、なぜ使うのですか?
関数や型が多くの型に対して動作しつつ、それらの間の関係を保てるようにします。
function first<T>(xs: T[]): T | undefined { return xs[0]; }
function pick<T, K extends keyof T>(o: T, k: K): T[K] { return o[k]; }
first([1, 2])はanyではなくnumber | undefinedを返します。pick(user, "name")はnameの型を返します。extends keyof Tの制約により、存在しないキーを渡すことはできません。
9. よく使うユーティリティ型は何ですか?
| ユーティリティ | 何をするか |
|---|---|
| Partial<T> | すべてのプロパティをオプショナルにする |
| Required<T> | すべてのプロパティを必須にする |
| Readonly<T> | すべてのプロパティをreadonlyにする |
| Pick<T, K> | 指定したキーだけを残す |
| Omit<T, K> | 指定したキーを取り除く |
| Record<K, V> | キーがK、値がVのオブジェクト型 |
| ReturnType<F> | 関数型の戻り値の型 |
10. マップ型とは何ですか?
別の型のキーをループして新しい型を構築します。Partial自体も{ [K in keyof T]?: T[K] }と書けます。これを知っていれば、ユーティリティ型を丸暗記しているのではなく理解していると示せます。
11. as constとは何ですか?
コンパイラに最も狭い型を推論させ、すべてをreadonlyにするよう指示します。const arr = [1, 2, 3] as constはreadonly [1, 2, 3]型になり、arr.push(4)はerror TS2339: Property 'push' does not exist on type 'readonly [1, 2, 3]'で失敗します。許可される値のリストや設定オブジェクトに役立ちます。
12. 型アサーションとは何で、いつ危険ですか?
value as Typeはコンパイラに「信じてくれ」と伝えます。実行時には何もチェックしません。間違っていれば、後でクラッシュとしてエラーが現れます。アサーションよりも、チェックによる絞り込みや型ガード関数を優先しましょう。
言語の挙動
13. null と undefined の違いは何ですか?
undefined は値が一度も代入されていないことを意味し、null は「値なし」を意図的に設定したことを意味します。strictNullChecks を有効にすると、これらは別々の型として扱われます。扱うにはオプショナルチェイニング(a?.b)と nullish 合体演算子(a ?? b)を使いましょう。|| と違い、?? は null と undefined のときだけフォールバックするので、0 や空文字列はそのまま保持されます。
14. 非 null アサーション ! とは何ですか?
value! は、その値が null でも undefined でもないとコンパイラに伝えるものです。as と同様、チェックは行われません。コンパイラより自分がよく分かっている場面では便利ですが、通常は実行時のチェックを入れたほうが安全です。
15. keyof は何をしますか?また型の位置での typeof とは?
keyof T は T のプロパティ名のユニオンを生成します。型の位置では、typeof x は値の型を取得するので、同じ型を繰り返し書かずに済みます。組み合わせると、keyof typeof obj で値として定義したオブジェクトのキーが得られます。
16. enum とユニオン型はどう比較されますか?
enum は実行時のオブジェクトと値を作り出します。"idle" | "loading" のような文字列リテラルのユニオンはコンパイル時に消去され、余分なコードは不要です。多くのチームはシンプルさから文字列リテラルのユニオンを好み、実行時のオブジェクトが必要なときに enum を使います。
ツールと実プロジェクト
17. tsconfig.json は何をしますか?またどのオプションが重要ですか?
どのファイルを含めるか、どうチェックするかをコンパイラに伝えます。面接で聞かれるオプションは、strict、target(出力する JavaScript のバージョン)、module と moduleResolution、noEmit(チェックのみ。別のツールがビルドする場合によく使われる)、esModuleInterop、skipLibCheck です。
18. API レスポンスなど、外部から来るものにどう型を付けますか?
型は実行時に消えるので、コンパイラはネットワークが返すものを検証できません。データを unknown として扱い、使う前にスキーマライブラリか手書きの型ガードで検証しましょう。パース済みの JSON に型をアサートすると、バグが隠れてしまいます。
19. JavaScript プロジェクトを TypeScript にどう移行しますか?
段階的に進めましょう。allowJs を付けた tsconfig.json を追加し、ファイルを1つずつリネームし、より厳しいオプションを徐々に有効にし、まず境界部分(API クライアントや共有ユーティリティなど)に型を付けます。一気に書き換えるのは避けましょう。
20. 構造的型付けとは何ですか?
TypeScript は型を名前ではなく形で比較します。オブジェクトが型の要求するフィールドを持っていれば、その型として宣言されていなくても受け入れられます。宣言された名前が重要となる公称型付けを使う言語とは異なります。
準備の方法
- playground か小さなプロジェクトを strict モードで開き、失敗するはずの例も含めて上記の各例を試しましょう。
- ジェネリック関数と判別可能ユニオンを記憶から書けるようにしておきましょう。
- any ではなく unknown を選ぶ理由を説明できるようにしましょう。
- 実プロジェクトで扱いにくいものに型を付けた経験を1つ用意しましょう。
関連する練習には、Node.js 面接質問、Angular 面接質問、Python 面接質問のガイドもご覧ください。
声に出して、自分の言葉で練習しましょう。後で文字起こしを見ると、どこで話がそれたり要点を飛ばしたりしたかが分かります。ライブの面接や評価では、AI ツールに関する雇用主のルールに従いましょう。
YesToTheOfferで回答を練習しましょう
声に出してリハーサルし、自分の履歴書から作られた回答を取得し、後で文字起こしを確認できます。30分無料、サブスクリプション不要。



