ウェブエンジニア問題集

基本の型 — プリミティブ・配列・オブジェクト

TypeScriptで型を書く最も基本的な方法は 型注釈(type annotation) です。 変数や関数の引数・戻り値に : 型名 を付けることで、「ここにはこの型の値しか入らない」と宣言します。

学習者学習者

えっ、全部の変数にいちいち型を書かなきゃいけないの…?それはちょっと面倒だなあ。

安心してください。実はTypeScriptは多くの型を自動で推論してくれるので、すべてに型注釈を書く必要はありません。この章では「型の書き方」を押さえつつ、「どこまで自動でやってくれるのか」も一緒に確認していきます。


プリミティブ型

JavaScriptのプリミティブ値に対応する型です。

const name: string = 'Alice';
const age: number = 25;
const isActive: boolean = true;

TypeScriptで使うプリミティブ型は stringnumberboolean の3つが中心です。他に nullundefinedbigintsymbol もありますが、まずはこの3つを押さえれば十分です。


型推論 — 書かなくても推論される

実は、すべての変数に型注釈を書く必要はありません。TypeScriptは初期値から型を推論してくれます。

// 型注釈あり
const name: string = 'Alice';
 
// 型注釈なし — TypeScriptが string と推論する
const name = 'Alice';

どちらも同じ型になります。初期値がある変数は、型注釈を省略しても問題ありません。 推論に任せた方がコードが簡潔になるので、明示が必要な場面以外は省略するのが一般的です。

先生先生

だから基本は「初期値があるものは省略」「引数や初期値なしの変数には型を書く」。さっきの“全部書くのは面倒”という心配は、半分くらい解決でしょ?

型注釈を書くべき場面

型推論が効かない、または意図と異なる推論をされる場合に型注釈を書きます。

// 初期値がないとき — 推論できないので型注釈が必要
let result: string;
 
// 関数の引数 — 推論されないので型注釈が必要
function greet(name: string) {
  return `こんにちは、${name}さん`;
}
 
// 推論結果を明示的に制限したいとき
let status: 'loading' | 'success' | 'error' = 'loading';
// 型注釈がないと string に推論されてしまう

配列の型

配列の型は 型名[] で書きます。

const numbers: number[] = [1, 2, 3];
const names: string[] = ['Alice', 'Bob'];
const flags: boolean[] = [true, false, true];

Array<number> というジェネリクス記法もありますが、number[] の方がよく使われます。どちらも同じ意味です。

// この2つは同じ
const numbers: number[] = [1, 2, 3];
const numbers: Array<number> = [1, 2, 3];

配列も型推論が効きます。

const numbers = [1, 2, 3]; // number[] と推論される
const mixed = [1, 'hello']; // (number | string)[] と推論される

オブジェクトの型

オブジェクトの型は、プロパティごとに型を指定します。

const user: { name: string; age: number } = {
  name: 'Alice',
  age: 25,
};

インラインで書くと長くなるので、通常は次の章で扱う型エイリアスインターフェースで名前を付けます。

オプショナルプロパティ

? を付けると、そのプロパティは省略可能になります。

const user: { name: string; age?: number } = {
  name: 'Alice',
  // ageは省略できる
};

age? は「number または undefined」を意味します。プロパティ自体が存在しない場合も許容されます。

readonlyプロパティ

readonly を付けると、そのプロパティは代入で変更できなくなります。

const user: { readonly name: string; age: number } = {
  name: 'Alice',
  age: 25,
};
 
user.name = 'Bob'; // コンパイルエラー — readonlyなので変更できない
user.age = 26; // OK

関数の型

関数の引数と戻り値に型を付けます。

function add(a: number, b: number): number {
  return a + b;
}

戻り値の型は推論されるので省略できる場合が多いです。

// 戻り値の型を省略 — TypeScriptが number と推論する
function add(a: number, b: number) {
  return a + b;
}

ただし引数の型は推論されません。常に書く必要があります。

アロー関数の型

const add = (a: number, b: number): number => {
  return a + b;
};
 
// 1行で返せる場合
const add = (a: number, b: number): number => a + b;

何も返さない関数 — void

関数が値を返さない場合、戻り値の型は void です。

function logMessage(message: string): void {
  console.log(message);
  // returnがない、または return; だけ
}

void は「戻り値を使わない」という意思表示です。推論に任せても構いませんが、明示すると意図が伝わりやすくなります。


特殊な型

any・unknown・neverの使い分けのイメージ

any / unknown / never は名前が似ていて混同しがちですが、役割はまったく違います。先に違いを表で押さえておきましょう。

ひとことで言うと型チェック主な使いどころ
anyなんでもアリ完全に無効移行時の一時しのぎ(基本は避ける)
unknown安全な any使う前にチェックを強制型が不明な外部データ
neverありえない値例外を投げる関数・網羅チェック

any — 型チェックを放棄する

any はあらゆる型を受け入れます。型チェックが完全に無効になります。

let value: any = 'hello';
value = 42; // OK
value = true; // OK
value.foo.bar; // コンパイルエラーにならない(実行時にエラーになる可能性あり)

any はTypeScriptの恩恵を捨てることを意味します。JavaScriptからの移行時に一時的に使うことはありますが、新規コードでは避けてください。

unknown — 安全なany

unknownany と同じくあらゆる型を受け入れますが、使う前に型チェックを強制される点が異なります。

let value: unknown = 'hello';
 
// そのままでは使えない
value.toUpperCase(); // コンパイルエラー
 
// 型を確認してから使う
if (typeof value === 'string') {
  value.toUpperCase(); // OK — この中ではstring型として扱える
}

外部からのデータ(APIレスポンスなど)の型が不明な場合は any ではなく unknown を使い、型の絞り込みを行うのが安全です。型の絞り込みについては05章で詳しく扱います。

never — ありえない状態を示す

never は「この値は存在しない」「この関数は正常に返らない」ことを示す型です。

// 必ず例外を投げる関数
function throwError(message: string): never {
  throw new Error(message);
}

never は直接使う場面は少ないですが、05章の判別可能なユニオンで「すべてのケースを網羅したか」のチェックに使えます。


まとめ

TypeScriptの型注釈は : 型名 で書きます。プリミティブ型(stringnumberboolean)、配列(型名[])、オブジェクト({ プロパティ: 型 })が基本です。

型推論により、初期値がある変数や関数の戻り値は型注釈を省略できます。引数の型は推論されないので常に書く必要があります。

any は型チェックを無効にするので新規コードでは避け、不明な型には unknown を使ってください。

次の章では、オブジェクトの型に名前を付ける型エイリアスインターフェースを扱います。