TypeScriptの基本型一覧:string・number・配列・unknownと型推論
TypeScriptの基本型、型推論、配列・タプル、nullとundefined、unknownとany、voidとneverをコードで解説。型チェックと実行時の動作の違いを整理します。
TypeScriptの型は、値の扱い方をコンパイル時に確認するための情報です。文字列に数値専用の操作を行うような間違いを見つけられますが、通信障害や不正な外部入力まで自動で防ぐものではありません。
この記事では基本型、型推論、配列・タプル、unknown、void、neverを整理します。例はTypeScript 4.9.4のstrict設定で確認します。インストール記事で用意する学習用プロジェクトを前提とし、各例を別ファイルに保存します。
基本型とデータ構造を表す
型注釈と型推論を使い分ける
ファイル:src/primitive-types.ts
export {};
let title = 'TypeScriptの基本'; // stringと推論される
let views: number = 10;
const published: boolean = true;
const status = 'draft'; // 文字列リテラル型 "draft"
views += 5;
title = title.toUpperCase();
console.log(title, views, published, status);
console.log(Number.isFinite(views)); // true
初期値から型が分かる場合、型注釈を省略できます。省略しただけで必ずanyになるわけではありません。letで宣言した上の文字列は後から別の文字列へ変更でき、constで宣言したstatusは具体的な文字列の型として扱われます。
通常は小文字のstring・number・booleanを使います。大文字で始まるStringなどのラッパー型と混同しないようにします。
numberには整数・小数のほか、NaNやInfinityも含まれます。正の整数だけを許すなら、Number.isInteger()や範囲の検証を追加します。booleanの値はtrueかfalseですが、JavaScriptの条件式では空文字や0なども偽として評価されます。型の違いと真偽への変換は別の話です。
文字列・配列・タプルを扱う
ファイル:src/collection-types.ts
export {};
const message: string = 'hello world';
const tags: string[] = ['typescript', 'nextjs'];
const point: readonly [number, number] = [10, 20];
console.log(message.length); // 11
console.log(message.slice(0, 5)); // hello
console.log(message.toUpperCase()); // HELLO WORLD
console.log(tags.map(tag => tag.toUpperCase()));
console.log(point[0] + point[1]); // 30
string[]は文字列を要素に持つ配列です。Array<string>とも書けます。タプルは位置ごとの型を表し、上の例では数値2個という組み合わせを表しています。readonlyは型チェック上の変更制限で、実行時に配列を凍結する機能ではありません。
文字列のlengthはUTF-16のコード単位を数えます。絵文字など、見た目の文字数と一致しない値もあります。画面の入力制限で「何文字」と数えるときは、要件に合った数え方を選びます。
通常の配列は空の場合もあるため、添字で取り出した値の存在に注意します。noUncheckedIndexedAccessを有効にすると、このような読み取りでundefinedを考慮しやすくなります。
オブジェクトとユニオンで候補を表す
ファイル:src/object-types.ts
export {};
type Article = {
id: string;
title: string;
summary?: string;
status: 'draft' | 'published';
};
const article: Article = {
id: 'post-1',
title: '型を使った設計',
status: 'draft',
};
console.log(article.summary ?? '要約はありません');
console.log(article.status === 'published' ? '公開中' : '下書き');
summary?: stringは省略可能な項目です。読み取りでは値がない可能性を考えます。'draft' | 'published'は、どちらかの値を取るユニオン型です。単なるstringよりも、許可する状態を具体的に表せます。
型エイリアスのtype Article = ...は名前を付ける仕組みで、実行時にクラスやオブジェクトを作りません。オブジェクト型はinterfaceでも定義できます。
nullとundefinedを明示的に扱う
ファイル:src/optional-values.ts
export {};
function repeat(message: string, times?: number): string[] {
const count = times ?? 1;
if (!Number.isInteger(count) || count < 0) {
throw new RangeError('回数は0以上の整数にしてください');
}
return Array.from({ length: count }, () => message);
}
let selectedId: string | null = null;
console.log(selectedId ?? '未選択');
console.log(repeat('確認')); // ["確認"]
console.log(repeat('確認', 0)); // []
console.log(repeat('確認', 2)); // ["確認", "確認"]
selectedId = 'post-1';
console.log(selectedId);
strictNullChecksが有効なら、通常のstringへnullやundefinedを代入できません。欠けた値を表す必要がある場合はユニオンに含めます。この設定はstrictで有効になる検査の一つです。
??はnullまたはundefinedの場合に右側を使います。times || 1では0まで1に置き換わってしまうため、例の回数指定には適しません。省略可能な引数は、関数の内部ではundefinedの可能性を持ちます。
anyとunknownの違いを理解する
ファイル:src/unknown-values.ts
export {};
function displayValue(value: unknown): string {
if (typeof value === 'string') {
return value.trim();
}
if (typeof value === 'number') {
return value.toFixed(1);
}
return '表示できない値です';
}
console.log(displayValue(' TypeScript ')); // TypeScript
console.log(displayValue(10)); // 10.0
console.log(displayValue(null)); // 表示できない値です
anyは、多くの型チェックを回避できる特別な型です。実行時に特別な型検査が追加されるわけではありません。たとえばanyの値に存在しないメソッドを呼んでも、コンパイラが止めず、実行時に失敗する場合があります。
unknownにもさまざまな値を代入できますが、型を確認せずにプロパティやメソッドを利用できません。外部入力など、まだ正体が分からない値を受け取るときに役立ちます。as stringという型アサーションだけでは、値の検証や文字列への変換は行われません。
voidとneverを混同しない
ファイル:src/return-types.ts
export {};
function report(message: string): void {
console.log(message);
}
function fail(message: string): never {
throw new Error(message);
}
report('確認を開始します');
try {
fail('入力が不正です');
} catch (error: unknown) {
console.log(error instanceof Error ? error.message : '不明なエラー');
}
voidは、主に呼び出し側が戻り値を利用しないことを表します。このreport()は値を返さないため、JavaScript上の結果はundefinedです。一方、関数型の() => voidには、実際には値を返す関数を代入できる場合があります。その値を呼び出し側が使わないという契約であり、voidとundefinedが常に同じ型という意味ではありません。
neverは値が存在しない型です。常に例外を投げる関数や、型の絞り込みで候補が残らなくなった場所に現れます。戻り値をneverにした関数でreturn 2と書くと型エラーです。「neverと書いたので、そのreturnが実行されなくなる」という動作にはなりません。型注釈はJavaScriptの制御フローを変更しないためです。
コンパイルして確認する
各ファイル先頭のexport {}は、そのファイルを独立したモジュールとして扱うための指定です。学習用の変数名が別のファイルやブラウザのグローバル名と衝突することを避けます。
npx tsc src/primitive-types.ts src/collection-types.ts src/object-types.ts src/optional-values.ts src/unknown-values.ts src/return-types.ts --strict --target ES2020 --module NodeNext --outDir dist --noEmitOnError
node dist/primitive-types.js
node dist/optional-values.js
node dist/unknown-values.js
node dist/return-types.js
このように入力ファイルを直接指定したコマンドでは、tsconfig.jsonは読み込まれません。プロジェクトの設定を使う場合はnpx tsc -p tsconfig.jsonを実行します。型チェックに加え、期待する値が実行時にも返ることを確認してください。
型注釈と型推論の使い分け
値から分かる型は推論を利用する
初期値が明確なローカル変数では、同じ型を繰り返して書く必要はありません。関数の入力や公開するデータの形など、利用側と共有したい境界には型を明示すると意図が伝わります。
外部入力は使う前に検証する
型注釈を書いても、フォームから数値が届くようになるわけではありません。文字列の変換、空文字の扱い、値の範囲を確認してから、アプリケーションが期待する値として利用します。