本ツールで使用しているオープンソースライブラリ

本ツールのコードには 1 個のオープンソースライブラリが含まれています。

TypeScript チートシート — クイックリファレンス

TypeScript 5 の構文、型システム、そして最もよく使うイディオムをまとめたクイックリファレンス。日常の約 80% をカバーします。

TS

TypeScript TypeScript 5.x

ECMAScript + 型 · マルチパラダイム・構造化 · JS の上にある静的(段階的)型付け

おすすめの学習パス

まずコンパイル環境(tsc / tsconfig)を整え、TS が JS のスーパーセットであることを理解する → 基本的な型注釈、共用型、インターフェースを習得 → 型システム(ジェネリック、型ガード、型操作)を深掘りする → class とモジュールでコードを整理する → 非同期と DOM / Node の型を扱う → 最後にビルド設定、テスト、デバッグを必要に応じて参照。FAQ セクションは後で読み返すと落とし穴回避に役立ちます。

1.Hello World とビルド環境

TypeScript をコンパイル・実行し、tsc、tsconfig、型検査の流れを理解する。

最小プログラム

TS は JS のスーパーセットです:有効な JS は有効な TS。型注釈を付けて tsc で JS にコンパイルします。

1
2
3
4
5
6
// hello.ts
const message: string = 'Hello, world!';
console.log(message);
// 型注釈:変数名の後の : string
// $ npx tsc hello.ts # コンパイルして hello.js を生成
// $ node hello.js

tsc コンパイル

tsc は .ts を .js にコンパイルします。--noEmit は型チェックのみ、--watch は変更を監視、--strict は厳格モード。

1
2
3
4
5
// $ npx tsc app.ts # 単一ファイルをコンパイル
// $ npx tsc --noEmit # 型チェックのみ
// $ npx tsc --watch # 監視して自動コンパイル
// $ npx tsc --strict # 厳格な型チェック
// $ npx tsc --outDir dist # 出力ディレクトリ

tsconfig.json

tsconfig.json でコンパイルを設定:target、module、strict、outDir。npx tsc は自動で読み込みます。

1
2
3
4
5
6
7
8
9
10
11
12
{
"compilerOptions": {
"target": "ES2022",
"module": "ESNext",
"moduleResolution": "bundler",
"strict": true, // 厳格モード
"outDir": "dist",
"sourceMap": true // デバッグ用ソースマップ
},
"include": ["src"]
}
// tsc --init でデフォルト設定を生成

TS を直接実行

tsx / ts-node を使うとコンパイルなしで TS を直接実行できます。Node 22 は --experimental-strip-types をネイティブサポート。

1
2
3
4
5
// $ npx tsx script.ts # TS を直接実行
// $ npx ts-node script.ts # 旧方式
// Node 22+:
// $ node --experimental-strip-types script.ts
// 開発スクリプトは tsx が一般的、本番は tsc でコンパイルして実行

厳格モード

strict: true ですべての厳格チェックを有効化:null チェック、暗黙 any のエラー、未使用変数の警告。新プロジェクトでは必須。

1
2
3
4
5
6
7
8
// strict には以下が含まれます:
// 1. strictNullChecks null を通常の型に代入不可
// 2. noImplicitAny 引数に型がない場合にエラー
// 3. noUnusedLocals 未使用の変数でエラー
function greet(name: string) { // 明示的な型
return 'Hi ' + name;
}
// strict を有効にしない場合、name は暗黙の any となりエラーにならない

TS と JS の関係

TS はコンパイル時に型をチェックし、コンパイル後の JS には型情報が残りません。型はコンパイル時にのみ存在し、実行時に消去されます。

1
2
3
4
5
6
7
8
interface User {
name: string;
age: number;
}
const u: User = { name: 'Nick', age: 30 };
// コンパイル結果(型は削除される):
// const u = { name: 'Nick', age: 30 };
// 型エラーはコンパイル時に検出され、実行には影響しない

依存関係と型パッケージ

ライブラリには型定義が必要です。@types/* は DefinitelyTyped コミュニティの型パッケージ。typescript はコンパイラのツール依存です。

1
2
3
4
5
6
// $ npm i -D typescript @types/node
// $ npm i -D @types/express # コミュニティ型
// ライブラリに付属する型:
// axios は直接 export するため、@types 不要
// // @ts-ignore コメント:次の行のチェックをスキップ(慎重に)
// import axios from 'axios'; 型定義あり

エディタ統合

VS Code は TS をネイティブサポート:ホバーで型表示、エラーは赤い波線、補完、リファクタリング。エラーメッセージはインラインで表示。

1
2
3
4
5
6
7
// VS Code ショートカット:
// ホバー:型を表示
// クイック修正:Ctrl+. (Cmd+.)
// シンボルの名前変更:F2
// 定義へジャンプ:F12
// 型エラーは Problems パネルに表示
// 設定:ワークスペースの tsconfig には `tsc` のバージョンを合わせる必要あり

2.変数と型注釈

型注釈、型推論、共用型、any / unknown、型アサーション。

型注釈

変数名の後に `: 型` で注釈します。注釈後は型が固定され、別の型を代入するとコンパイルエラー。

1
2
3
4
5
6
const name: string = 'Nick';
let age: number = 30;
const isAdmin: boolean = true;
// エラー例:
// age = 'thirty'; // 型エラー
// 初期化後は型推論が効くため明示不要

型推論

TS は初期化値から型を推論し、多くの場合明示的な注釈は不要。複雑な型は明示的なインターフェースが望ましい。

1
2
3
4
5
6
7
8
const name = 'Nick'; // string と推論
let count = 42; // number
const arr = [1, 2, 3]; // number[]
const obj = { a: 1 }; // { a: number }
// 代入時のユニオン推論:
let status = 'idle' as const;
// 複雑なシナリオでは明示注釈が分かりやすい:
let data: Map<string, User> = new Map();

共用型

`|` は共用型を表します:`string | number` のいずれでも可。メンバーにアクセスするにはまず型ガードが必要。

1
2
3
4
5
6
7
8
9
10
11
12
function print(value: string | number) {
// ユニオン型は共通メンバーのみアクセス可
console.log(value.toString());
// 分岐処理:
if (typeof value === 'string') {
console.log(value.toUpperCase()); // string
} else {
console.log(value.toFixed(2)); // number
}
}
// リテラルユニオン:
type Dir = 'up' | 'down' | 'left' | 'right';

any と unknown

any は型チェックを無効化(避ける)。unknown は未知を意味し、ガード後にのみ使用可能。外部データを安全に扱うには unknown を使う。

1
2
3
4
5
6
7
8
9
let risky: any = 'text'; // any:チェックなし、慎重に
risky.method(); // コンパイルは通るがランタイムでクラッシュの可能性
// unknown:安全
let data: unknown = getApi();
if (typeof data === 'string') {
console.log(data.length); // ガード後に使用可
}
// unknown のアサーション:data as string
// any より unknown を優先

型アサーション

`as` はコンパイラに「あなたが分かっている」と伝えるアサーション。実行時は変わらず、コンパイル時の宣言に過ぎません。濫用はエラーを隠します。

1
2
3
4
5
6
7
const el = document.getElementById('btn') as HTMLButtonElement;
// 二重アサーションは非推奨(落とし穴):
// const n = value as unknown as number;
// より安全:まずガード、それからアサーション
const json = JSON.parse(text) as User[];
// as const:リテラルへの絞り込み
const modes = ['dev', 'prod'] as const;

非 null アサーション

`!` サフィックスは値が非 null / undefined だとアサートします。確信がある時のみ使用。そうでないと実行時にクラッシュする可能性。

1
2
3
4
5
6
7
8
9
let name: string | null = getMaybe();
const len = name!.length; // 非 null アサーション(リスクあり)
// より安全な書き方:
if (name) {
const l2 = name.length;
}
// または null 合体:
const l3 = name?.length ?? 0;
// 非 null アサーションはコンパイル時の約束、ランタイムで null だとクラッシュ

リテラル型

リテラル型は型を具体的な値に絞り込みます:'up'、42、true。共用と組み合わせてオプションを列挙。

1
2
3
4
5
6
7
8
let direction: 'up' | 'down' = 'up';
// direction = 'sideways'; // エラー:ユニオンに含まれない
const yes: true = true;
// オブジェクトプロパティの絞り込み:
const config = {
mode: 'production',
} as const; // mode: 'production' リテラル
// as const でオブジェクトメンバーを読み取り専用リテラル型に

分割代入と型

分割代入は型を保持します。関数の引数を分割代入する場合は引数オブジェクト全体の型を注釈する必要があります。

1
2
3
4
5
6
7
8
9
10
interface User { name: string; age: number; }
const { name, age }: User = getUser();
// 関数の引数で分割代入:
function show({ name, age }: User) {
console.log(name, age);
}
// 配列の分割代入:
const [first, second] = [1, 2] as const;
// オプションプロパティの分割代入:
const { name = 'guest' }: { name?: string } = data;

3.型システム

基本型、オブジェクト / 配列 / タプル、インターフェース、ジェネリック、型エイリアス、型操作。

基本型

string、number、boolean、null、undefined、void、symbol、bigint の基本型セット。

1
2
3
4
5
6
7
8
const s: string = 'text';
const n: number = 42;
const b: boolean = true;
const v: void = undefined; // 戻り値なしの関数
const nl: null = null;
const u: undefined = undefined;
const sym: symbol = Symbol('id');
const big: bigint = 10n;

配列とタプル

`number[]` は配列、`[string, number]` はタプル(長さと順序固定)、`readonly` は読み取り専用配列。

1
2
3
4
5
6
7
8
9
const nums: number[] = [1, 2, 3];
const strs: Array<string> = ['a', 'b']; // ジェネリック構文
// タプル:
let pair: [string, number] = ['age', 30];
// pair[0] = 42; // エラー:型が一致しない
// 読み取り専用配列:
const fixed: readonly number[] = [1, 2];
// fixed.push(3); // エラー:読み取り専用
// オプショナルタプル要素:type T = [string, number?]

オブジェクト型

オブジェクト型は形状を記述します:プロパティ、オプショナル `?`、読み取り専用 `readonly`、メソッドシグネチャ。

1
2
3
4
5
6
7
8
9
10
11
12
interface Point {
readonly x: number; // 読み取り専用
y: number;
label?: string; // オプショナル
}
const p: Point = { x: 1, y: 2 };
// p.x = 10; // エラー:readonly
// メソッド:
interface Greeter {
greet(name: string): string;
// または greet: (name: string) => string;
}

interface と type

`interface` はオブジェクトの形状を定義(拡張可)、`type` 別名はより柔軟(共用 / 交差 / タプル)。日常的には interface 優先。

1
2
3
4
5
6
7
8
9
10
11
interface User {
name: string;
}
// interface はマージ/継承可能:
interface Admin extends User {
permissions: string[];
}
// type エイリアス:
type ID = string | number; // ユニオン
type Pair = [string, number]; // タプル
type Shape = { area: number } & { color: string }; // 交差

列挙型

`enum` は名前付き定数集合:数値列挙、文字列列挙、const enum。文字列列挙がよく使われます。

1
2
3
4
5
6
7
8
9
10
11
12
13
enum Color {
Red, // 0
Green, // 1
Blue, // 2
}
enum Status {
Active = 'active',
Inactive = 'inactive',
}
const c: Color = Color.Green;
const s: string = Status.Active; // 'active'
// 逆引きマッピング:Color[0] === 'Red'(数値 enum)
// 型だけ欲しい場合:type S = 'active' | 'inactive'

ジェネリック

ジェネリックは型をパラメータ化します:T は型パラメータ。関数 / クラス / インターフェースで再利用可能。コンパイル時に確定。

1
2
3
4
5
6
7
8
9
10
11
function identity<T>(value: T): T {
return value;
}
const s = identity('hello'); // string
const n = identity(42); // number
// ジェネリック interface:
interface Box<T> {
value: T;
}
const box: Box<number> = { value: 42 };
// 複数パラメータ:function pair<A, B>(a: A, b: B)

keyof とインデックス

`keyof` はオブジェクトのキーの共用を取得、`T[K]` はインデックスアクセス、 mapped type。型操作のコアツール。

1
2
3
4
5
6
7
8
9
10
interface User { name: string; age: number; }
type Keys = keyof User; // 'name' | 'age'
// インデックスアクセス:
type NameType = User['name']; // string
// マップ型:
type Readonly<T> = {
readonly [K in keyof T]: T[K];
};
// Partial<T>、Required<T>、Pick<T,K> は組み込みユーティリティ
// 一部オプショナル:type PartialUser = Partial<User>

組み込みユーティリティ型

Partial / Omit / Pick / Record / Exclude / ReturnType などの mapped type でよくある変換を簡略化。

1
2
3
4
5
6
7
type PartialUser = Partial<User>; // すべてオプショナル
type PickName = Pick<User, 'name'>; // name のみ取得
type NoAge = Omit<User, 'age'>; // age を除外
type Rec = Record<string, number>; // キーと値のマッピング
type WithoutZero = Exclude<0 | 1 | 2, 0>; // 1 | 2
type R = ReturnType<typeof fn>; // 関数の戻り型
// Parameters<typeof fn> は引数の型

テンプレートリテラル型

テンプレート文字列構文で文字列型を構築。共用と組み合わせて順列を生成。文字列を型レベルで解析するテクニック。

1
2
3
4
5
6
7
8
type Event = `on${'Click' | 'Hover'}`;
// Event = 'onClick' | 'onHover'
type Size = `${'small' | 'large'}-${number}`;
// Size = 'small-1' | 'large-2' ...
// 文字列からの抽出:
// type Extracted = 'a:b'.split<'a:b', ':'>; // 型レベルの split
// 簡単な文字列パース:
// type First = 'abc' extends `${infer F}bc` ? F : never; // 'a'

4.型ガードと null 値

型ガード、型の絞り込み、オプショナルチェイニング、null 処理、参照セマンティクス。

型ガード

`typeof`、`instanceof`、`in` で共用型を絞り込みます。分岐内では型が自動的に絞り込まれます。

1
2
3
4
5
6
7
8
9
10
11
function f(v: string | number | Date) {
if (typeof v === 'string') {
v.toUpperCase(); // string
} else if (v instanceof Date) {
v.getTime(); // Date
} else {
v.toFixed(2); // number
}
}
// in ガード:
if ('permissions' in user) { /* Admin */ }

型の絞り込み

ガードの後、型はスコープ内で絞り込まれます(narrowing)。null チェックや truthy チェックでも絞り込まれます。

1
2
3
4
5
6
7
8
9
10
11
let value: string | null = getMaybe();
if (value) {
value.length; // string(絞り込み)
}
// 真偽値での絞り込み:
function f(s: string | undefined) {
s ?? console.log('missing'); // null 合体での絞り込み
}
// null チェック後:
value = null;
if (value === null) return; // 以降 value は非 null

判別共用

判別共用:判別フィールド(kind / type)を共有し、`switch` 後に型が精密に絞り込まれる。ステートマシンのモデリング。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
type Shape =
| { kind: 'circle'; radius: number }
| { kind: 'square'; side: number };
function area(s: Shape): number {
switch (s.kind) {
case 'circle': return Math.PI * s.radius ** 2;
case 'square': return s.side * s.side;
// 網羅性チェック:
default: {
const _exhaustive: never = s;
return _exhaustive;
}
}
}

オプショナルチェイニングと null 合体

`?.` で安全アクセス、`??` で null フォールバック、`??=` でデフォルト代入。深いチェインアクセスで null クラッシュを防止。

1
2
3
4
5
6
7
8
9
const name = user?.profile?.name ?? 'guest';
const count = data?.items?.length ?? 0;
// null 合体代入:
let settings = getConfig();
settings ??= { theme: 'dark' };
// オプショナル呼び出し:
callback?.();
// ?? と || の違い:
// 0 ?? 'x' は 0;0 || 'x' は 'x'

null と undefined

strictNullChecks 下では null / undefined を通常の型に代入できません。明示的な共用または処理が必要。

1
2
3
4
5
6
7
8
9
let name: string | null = null; // ユニオンに null を含む
let title: string | undefined;
// 関数の戻り値が null 可能性あり:
function find(): User | null {
return Math.random() > 0.5 ? null : { name: 'x' };
}
const u = find();
if (u) { u.name; } // 絞り込み後にアクセス
// 安全なアサーション:u!.name または u ?? { name: '?' }

参照セマンティクス

TS は JS の参照セマンティクスを変えません:オブジェクトは参照共有、配列はシャローコピー。型は形状を記述するのみ。

1
2
3
4
5
6
7
8
9
const a = { x: 1 };
const b = a; // 参照共有
b.x = 99;
console.log(a.x); // 99
// コピーしてもシャローコピー:
const c = { ...a };
c.x = 1; // a.x は不変
// TS の型は不変性を保証しない(readonly 必要)
// 深い凍結は as const + ライブラリで

カスタム型ガード

`is` 構文は関数を型ガードとして宣言します:boolean を返し引数を絞り込む。配列フィルタリングでよく使われます。

1
2
3
4
5
6
7
8
9
function isString(v: unknown): v is string {
return typeof v === 'string';
}
const values: unknown[] = ['a', 1, 'b', null];
const strs = values.filter(isString);
// strs の型は string[](ガードが効く)
// アロー関数での書き方:
const s2 = values.filter(
(v): v is string => typeof v === 'string');

表明関数

`asserts` 表明関数は不変条件を宣言します:void を返すが呼び出し後に型が絞り込まれる。エラーを投げて中断。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function assertString(v: unknown): asserts v is string {
if (typeof v !== 'string') {
throw new Error('期望 string');
}
}
function process(v: unknown): void {
assertString(v);
v.toUpperCase(); // string に絞り込まれる
}
// 無条件アサーション:
function assert(cond: unknown): asserts cond {}
// 値のアサーション:
function assertNonNull<T>(v: T): asserts v is NonNullable<T> {}
// ランタイム不変条件 + 型絞り込みの二重保険

5.制御フロー

分岐、ループ、switch と型の絞り込みの組み合わせ。

if / else

`if` / `else` 分岐と型ガード。条件式で変数の型が絞り込まれます。

1
2
3
4
5
6
7
8
9
function describe(v: string | number) {
if (typeof v === 'string') {
return `字符串: ${v.toUpperCase()}`;
} else {
return `数字: ${v.toFixed(2)}`;
}
}
// 多分岐 else-if + ガードで段階的に絞り込み
// truthy チェック:if (value) で非空に絞り込み

switch と網羅性

`switch` で判別共用を処理。`default` 分岐で `never` を使い全ケース網羅をチェック。

1
2
3
4
5
6
7
8
9
10
11
12
13
type Action =
| { type: 'add'; n: number }
| { type: 'reset' };
function reducer(a: Action) {
switch (a.type) {
case 'add': return a.n;
case 'reset': return 0;
default: {
const exhaustive: never = a; // 分岐不足はコンパイルエラー
return exhaustive;
}
}
}

ループ

`for` / `for...of` / `while` で反復。配列の反復は `for...of`、インデックスが必要な場合は `for` または `entries`。

1
2
3
4
5
6
7
8
9
for (let i = 0; i < 10; i++) { }
for (const item of items) { }
for (const [i, v] of items.entries()) {
console.log(i, v);
}
let n = 0;
while (n < 5) { n++; }
// オブジェクトキーの反復:
for (const key of Object.keys(obj) as (keyof typeof obj)[]) { }

三項演算子と型

三項演算子の両側の型は共用されます。条件で異なる型を返す場合、結果は共用型になります。

1
2
3
4
5
6
const result = cond ? 'yes' : 0;
// result の型は 'yes' | 0(リテラルユニオン)
// 型を統一したい場合は明示注釈:
const msg: string = cond ? 'yes' : String(0);
// ネストした三項は可読性が悪いのでガードを使う
// null 値:v ?? fallback

break と continue

`continue` は現在のループをスキップ、`break` は抜ける、ラベル付き `break` / `continue` でネストを制御。

1
2
3
4
5
6
7
8
9
10
11
for (let i = 0; i < 10; i++) {
if (i % 2 === 0) continue;
if (i > 7) break;
}
outer:
for (const a of list) {
for (const b of a.items) {
if (b.done) continue outer;
}
}
// 型は変わらず、純粋な制御フロー

早期 return

ガード節で早めに return してネストを減らす。null チェック後に型が絞り込まれます。

1
2
3
4
5
6
7
8
function process(u: User | null) {
if (u === null) return; // 早期リターン
if (u.age < 18) return;
console.log(u.name); // 非 null に絞り込まれている
}
// 複数のガードでメインロジックを平坦に
// null 合体で早期にデフォルト値を:
const name = u?.name ?? 'guest';

do...while

`do...while` は先に一度実行してから判定。少なくとも 1 回実行するループで使用。型は関与しません。

1
2
3
4
5
6
7
8
9
10
11
let attempts = 0;
do {
attempts++;
const ok = tryOnce();
if (ok) break;
} while (attempts < 3);
// 最低 1 回実行し、その後条件を判定
// while:先に判定してから実行(0 回もあり得る)
// 使用シーン:
// リトライ、メニュー選択、入力バリデーション
// 無限ループに注意:条件は最終的に false になる必要あり

オブジェクト走査

`Object.keys` でオブジェクトのキーを反復。`keyof` のアサーションで型安全に。値は `Object.values`。

1
2
3
4
5
6
7
8
9
10
11
const config = { host: 'x', port: 3000 };
// キーの反復(アサーション必要):
for (const key of Object.keys(config) as (keyof typeof config)[]) {
console.log(key, config[key]);
}
// 値の反復:
for (const value of Object.values(config)) { }
// キーと値のペア:
for (const [k, v] of Object.entries(config)) { }
// 型の注意点:
// Object.keys は string[] を返すため、アサーション後に安全にアクセス

6.関数

関数の注釈、オプショナル / デフォルト引数、オーバーロード、残余引数、this。

関数の型

関数の型注釈:引数の型と戻り値の型。アロー関数の型は関数宣言と同等。

1
2
3
4
5
6
7
8
9
10
11
// 関数宣言:
function add(a: number, b: number): number {
return a + b;
}
// アロー関数:
const add = (a: number, b: number): number => a + b;
// 関数型の変数:
type Fn = (a: number, b: number) => number;
const f: Fn = add;
// void 戻り値:
function log(msg: string): void { console.log(msg); }

オプショナルとデフォルト引数

`?` はオプショナル引数、`=` はデフォルト引数。デフォルトは暗黙的にオプショナル。オプショナル引数は必須引数の後。

1
2
3
4
5
6
7
8
9
10
11
function greet(name: string, title?: string): string {
return title ? `${title} ${name}` : name;
}
// デフォルト引数:
function mul(a: number, b = 2): number {
return a * b;
}
mul(3); // 6
// デフォルト引数は省略可能
// オプショナル引数は後ろに配置:
// greet('Nick', undefined) も可

残余引数

`...rest` は可変長の引数を配列に集めます。rest 引数には配列の型注釈が必要。

1
2
3
4
5
6
7
8
9
function sum(...nums: number[]): number {
return nums.reduce((a, b) => a + b, 0);
}
sum(1, 2, 3); // 6
// ジェネリック rest でタプルを保持:
function tuple<T extends unknown[]>(...args: T): T {
return args;
}
const t = tuple(1, 'a', true); // [number, string, boolean]

オーバーロード署名

オーバーロード:複数の署名宣言 + 1 つの実装。呼び出しは署名で照合する。引数パターンで戻り型を制約。

1
2
3
4
5
6
7
8
function pick(obj: Record<string, unknown>, key: string): unknown;
function pick(obj: number[], index: number): number;
function pick(obj: any, key: string | number): unknown {
return obj[key];
}
// 実装シグネチャは外部から見えない
// オーバーロードは順にマッチ、緩い型は最後に
// 戻り値型が異なるケース:DOM API 型でよく使われる

ジェネリック関数

ジェネリック引数の制約:`T extends ...`。制約により型を絞り、そのメンバーを使える。

1
2
3
4
5
6
7
8
9
10
function first<T extends string | number[]>(arr: T): T[number] {
return arr[0];
}
const s = first('hello'); // string
const n = first([1, 2, 3]); // number
// 制約付き呼び出し:
function getLen<T extends { length: number }>(v: T): number {
return v.length;
}
// 複数ジェネリック:<K, V extends keyof K>

this 型

`this` 引数で `this` の型を注釈。メソッドチェーンは `this` を返してチェインを実現。アロー関数は `this` を束縛しない。

1
2
3
4
5
6
7
8
9
10
11
12
13
class Builder {
private items: string[] = [];
add(item: string): this {
this.items.push(item);
return this; // チェーン
}
}
const b = new Builder().add('a').add('b');
// 明示的な this パラメータ(先頭に):
function log(this: { name: string }) {
console.log(this.name);
}
// アロー関数は外側の this を継承

コールバックと関数引数

コールバック関数を引数として扱うには関数型で注釈する。配列の高階関数である map / filter / reduce の型推論。

1
2
3
4
5
6
7
8
9
function withLog(fn: (n: number) => number) {
return fn(42);
}
withLog(n => n * 2); // 引数型は自動推論
// 配列の高階関数:
const doubled = [1, 2, 3].map(n => n * 2);
const evens = [1, 2, 3, 4].filter(n => n % 2 === 0);
const total = [1, 2, 3].reduce((acc, n) => acc + n, 0);
// コールバックの this 型に注意、ロストしないように

関数の制約テクニック

引数の共用絞り込み、オプショナルコールバック、戻り推論。関数の引数は具体クラスよりインターフェースが望ましい。

1
2
3
4
5
6
7
8
9
10
function handle(v: string | number, cb?: (r: string) => void) {
const r = typeof v === 'string' ? v.toUpperCase() : String(v);
cb?.(r); // オプショナルコールバック
}
// 引数には interface(構造的型):
interface HasId { id: number }
function findById<T extends HasId>(arr: T[], id: number): T | undefined {
return arr.find(x => x.id === id);
}
// 構造的型:形状が合えばよく、同一インスタンスでなくてよい

7.文字列

テンプレート文字列、よく使うメソッド、正規表現、文字処理。

テンプレート文字列

バッククォートのテンプレート文字列:`${}` で補間、複数行を保持。型は依然として `string`。

1
2
3
4
5
6
7
8
9
10
11
12
const name = 'Nick';
const greeting = `Hello, ${name}!`;
// 複数行:
const lines = `
line 1
line 2
`;
// 式:
const total = `Sum: ${1 + 2}`;
// テンプレートリテラル型:
// type T = `id-${string}`
// 型は依然として string、テンプレートはシンタックスシュガー

よく使うメソッド

`slice` / `substring` で切り出し、`toUpperCase` で変換、`split` で分割、`includes` / `startsWith` で検索、`replace` で置換。

1
2
3
4
5
6
7
8
9
const s = 'TypeScript';
const sub = s.slice(0, 4); // 'Type'
const upper = s.toUpperCase(); // 'TYPESCRIPT'
const parts = s.split(''); // 文字配列
s.includes('Script'); // true
s.startsWith('Type'); // true
s.endsWith('t'); // true
const r = 'a-b-c'.replace(/-/g, '_'); // 'a_b_c'
const padded = '7'.padStart(3, '0'); // '007'

テンプレートリテラル型

型レベルのテンプレート文字列:`${}` で型を連結。`infer` で文字列構造を抽出。

1
2
3
4
5
6
7
8
type Route = `/users/${string}/profile`;
const good: Route = '/users/123/profile';
// 型レベルの抽出:
type ExtractId<S extends string> =
S extends `/users/${infer Id}/profile` ? Id : never;
type Id = ExtractId<'/users/42/profile'>; // '42'
// 大文字変換:Uppercase<T>、Lowercase<T>
// 組み合わせ:type All = `${'a'|'b'}${'1'|'2'}` // 'a1'|'a2'|'b1'|'b2'

文字とコードポイント

`length` は UTF-16 コード単位数(絵文字は 2 つと数える)。コードポイントの反復は `for...of` / `Array.from`。

1
2
3
4
5
6
7
8
9
const emoji = '👋';
console.log(emoji.length); // 2(サロゲートペア)
console.log([...emoji].length); // 1
const s = 'abc';
for (const ch of s) { } // コードポイント単位
// コードポイントアクセス:
const first = Array.from(s)[0];
// charCodeAt/fromCodePoint でコードポイントを処理:
String.fromCodePoint(128075); // '👋'

正規表現

`RegExp` でマッチ・置換。`match` はキャプチャグループの配列を返し、`matchAll` はグローバルに反復。型レベルでは `RegExp` は固定。

1
2
3
4
5
6
7
8
9
10
11
const re = /(\w+)@(\w+)/g;
const all = s.matchAll(re);
for (const m of all) {
console.log(m[1], m[2]); // キャプチャグループ
}
// キャプチャ付き置換:
const r = '2026-01-01'.replace(/(\d{4})-(\d{2})-(\d{2})/, '$3/$2/$1');
// 非 null アサート:
const match = s.match(/(\w+)@/);
if (match) { console.log(match[1]); }

ロケールと比較

`localeCompare` でロケール比較、`toLocaleLowerCase` でロケール変換、`Intl` で数値や日付をフォーマット。

1
2
3
4
5
6
const arr = ['ä', 'a', 'z'].sort((a, b) => a.localeCompare(b, 'zh'));
const num = (1234.5).toLocaleString('zh-CN'); // '1,234.5'
const date = new Date().toLocaleDateString('zh-CN');
// Intl.NumberFormat:
const nf = new Intl.NumberFormat('zh-CN', { style: 'currency', currency: 'CNY' });
// ロケール一覧:Intl.supportedValuesOf('language')

エスケープ文字

文字列エスケープ:`\n` 改行、`\t` タブ、`\\` バックスラッシュ、`\u` コードポイント。シングルクォート / ダブルクォート内でエスケープ。

1
2
3
4
5
6
7
8
9
10
const nl = '第一行\n第二行';
const tab = 'a\tb'; // a スペースタブ b
const backslash = 'C:\\path'; // C:\path
const quote = 'He said \'hi\'';
const uni = '\u4e2d'; // '中'
const code = '\u{1F600}'; // emoji コードポイント
// テンプレート文字列はシングルクォート/ダブルクォートのエスケープ不要
const tmpl = `She said "hi" and 'bye'`;
// よく使う:\n 改行 \t インデント \\ パス
// JSON 出力ではクォートをエスケープする必要あり

反転と比較

文字列反転は split / 配列、空白除去、重複除去。比較は `localeCompare` または正規化比較。

1
2
3
4
5
6
7
8
9
10
11
12
const s = 'hello';
// 反転:
const rev = [...s].reverse().join(''); // 'olleh'
// 先頭末尾の空白を除去:
const t = ' text '.trim();
// すべての空白を除去:
const compact = s.replace(/\s+/g, '');
// 繰り返し:'ab'.repeat(3) // 'ababab'
// 回文判定:
const isPalindrome = s === [...s].reverse().join('');
// 正規化比較(大文字小文字を無視):
s.toLowerCase() === 'HELLO'.toLowerCase()

8.コレクションとオブジェクト

配列、オブジェクト、Map / Set、不変更新パターン。

配列操作

`push` / `pop` で末尾、`unshift` / `shift` で先頭、`splice` で挿入 / 削除、`slice` でコピー、`includes` / `indexOf` で検索。

1
2
3
4
5
6
7
8
9
const arr = [1, 2, 3];
arr.push(4); // [1,2,3,4]
const last = arr.pop(); // 4
arr.unshift(0); // [0,1,2,3]
const first = arr.shift(); // 0
const removed = arr.splice(1, 1); // 削除
const copy = arr.slice(); // シャローコピー
arr.includes(2);
arr.indexOf(2); // 最初の位置または -1

map / filter / reduce

関数型反復:`map` で変換、`filter` で絞り込み、`reduce` で集約、`find` で検索、`every` / `some` で判定。新しい配列を返し元は変更しない。

1
2
3
4
5
6
7
8
9
const nums = [1, 2, 3, 4];
const doubled = nums.map(n => n * 2);
const evens = nums.filter(n => n % 2 === 0);
const sum = nums.reduce((acc, n) => acc + n, 0);
const found = nums.find(n => n > 2); // 3 | undefined
const ok = nums.every(n => n > 0); // true
const has = nums.some(n => n === 4); // true
// チェーン:
nums.filter(n => n % 2).map(n => n * 10).reduce((a, b) => a + b, 0);

オブジェクト操作

スプレッドで `{...a, ...b}` マージ、`Object.keys` / `values` / `entries` で反復、`keyof` で型付きキー。

1
2
3
4
5
6
7
8
9
10
const base = { id: 1, name: 'Nick' };
const extended = { ...base, age: 30 }; // マージ
const override = { ...base, name: 'New' }; // 上書き
const keys = Object.keys(base);
// 型安全な反復:
for (const key of Object.keys(base) as (keyof typeof base)[]) {
console.log(base[key]);
}
// entries:
for (const [k, v] of Object.entries(base)) { }

Map

`Map` は任意のキーでマッピング:set / get / has / delete / size。挿入順を保持し、O(1) 検索。

1
2
3
4
5
6
7
8
9
10
11
const scores = new Map<string, number>();
scores.set('alice', 90);
const v = scores.get('alice'); // number | undefined
scores.has('bob'); // false
scores.delete('alice');
scores.size;
// 反復:
for (const [k, val] of scores) { }
for (const key of scores.keys()) { }
// 初期化:
new Map([['a', 1], ['b', 2]]);

Set

`Set` は重複排除:add / delete / has / size。配列の重複排除、和集合 / 差集合。

1
2
3
4
5
6
7
8
9
10
const set = new Set<number>();
set.add(1).add(2).add(1); // {1, 2}
set.has(1); // true
set.delete(2);
// 配列の重複排除:
const unique = [...new Set([1, 2, 2, 3])]; // [1, 2, 3]
// 和集合:
const union = new Set([...a, ...b]);
// 積集合:
const inter = new Set([...a].filter(x => bSet.has(x)));

不変更新

その場変更ではなくスプレッド / コピーで更新。オブジェクト置換、配列追加 / 削除は新しい参照を返す。React の state でよく使われる。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
interface State {
items: string[];
count: number;
}
const next: State = {
...state,
items: [...state.items, 'new'], // 追加
count: state.count + 1,
};
// 1 件削除:
const filtered = state.items.filter(x => x !== 'old');
// 1 件更新:
const updated = state.items.map((x, i) => i === 0 ? 'new' : x);
// readonly で型チェックによる in-place 変更防止

タプルと Record

タプルは長さ固定、`Record` はキーと値のマッピング。オブジェクトリテラルを `as const` で定数化。

1
2
3
4
5
6
7
8
9
10
11
const point: [number, number] = [10, 20];
const [x, y] = point; // 分割代入
// Record:
type Config = Record<string, boolean>;
const flags: Config = { debug: true };
// as const 定数オブジェクト:
const statusMap = {
active: '运行中',
stopped: '已停止',
} as const;
// statusMap.active の型は '运行中' リテラル

WeakMap / WeakSet

`WeakMap` / `WeakSet` のキーはオブジェクトかつ弱参照。GC を妨げない。メタデータキャッシュや副作用記録に使用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const meta = new WeakMap<object, { visited: boolean }>();
const node = document.getElementById('a');
if (node) meta.set(node, { visited: true });
// キーはオブジェクトのみ:
// meta.set(1, {}) // エラー
// 反復不可(keys/size なし)
// 用途:
// 1. オブジェクトにプライベートメタデータを紐付ける
// 2. 計算結果をキャッシュしてリークを防ぐ
// 3. リスナー用フラグ
// WeakSet は集合のマーキング用:
const processed = new WeakSet<object>();
processed.add(obj);
processed.has(obj); // true

9.パフォーマンスとメモリ

ガベージコレクション下のメモリ観、大規模データ、文字列最適化、監視。

ガベージコレクション

TS / JS には GC があり、手動解放は不要。参照されなくなったオブジェクトは回収される。クロージャによる長寿命オブジェクトの保持に注意。

1
2
3
4
5
6
7
8
9
10
// オブジェクトはスコープ外かつ参照なしで GC される
function create() {
const big = new Array(1e6);
return () => big.length; // クロージャが big を保持
}
// クロージャが big を生かしている:
const f = create(); // big は解放不可
// 解放:
f = null; // 参照を切る
// グローバルキャッシュが無限に膨らむのを避ける

大規模配列

大規模配列では TypedArray やストリーミングを検討。`filter` / `map` は新配列を生成するので注意。単一ループの再利用が効率的。

1
2
3
4
5
6
7
8
9
10
// TypedArray でバイナリ処理:
const buf = new Float64Array(1e6);
// 大きな配列で map/filter の連鎖を避ける:
// BAD:複数パス
// GOOD:1 パスで処理
const src = new Array(1e6).fill(0);
let sum = 0;
for (let i = 0; i < src.length; i++) sum += src[i];
// バイナリ:DataView + ArrayBuffer
// 数値精度:BigInt で大整数、BigInt.asIntN で切り詰め

文字列のメモリ

文字列は不変で、連結は新しい文字列を生成する。大量連結は配列 `join` かテンプレート。文字列はインターンされる。

1
2
3
4
5
6
7
8
9
// ループでの文字列連結は遅い:
let s = '';
for (let i = 0; i < 1e5; i++) s += i; // BAD
// 配列の join を使う:
const parts: string[] = [];
for (let i = 0; i < 1e5; i++) parts.push(String(i));
const s2 = parts.join(''); // GOOD
// またはテンプレート文字列で分割
// 長い文字列のスライスは slice(O(n))

WeakRef とキャッシュ

WeakRef は弱参照でオブジェクトの回収を妨げず、WeakMap / WeakSet もキーが弱参照される。キャッシュや副作用を記録する用途に弱コンテナを使う。

1
2
3
4
5
6
7
8
9
const cache = new WeakMap<object, number>();
const obj = { id: 1 };
cache.set(obj, compute(obj));
// obj が GC されるとエントリも自動で消える
// WeakRef:
const ref = new WeakRef(obj);
const alive = ref.deref(); // object | undefined
// FinalizationRegistry で回収を監視:
const reg = new FinalizationRegistry(held => console.log('collected', held));

パフォーマンスのコツ

`any` による最適化低下を避け、再割り当てを減らし、結果をキャッシュ。V8 の最適化は安定した形状のオブジェクトに依存。

1
2
3
4
5
6
7
8
9
10
11
12
13
// 動的プロパティでオブジェクト形状を変えない:
// BAD:
const obj: Record<string, number> = {};
obj.a = 1; obj.b = 2; // 形状変化
// GOOD:完全な形状を宣言
const obj = { a: 0, b: 0 };
// 長いチェーンをキャッシュ:
const len = arr.length; // ループ内で再取得しない
// 暗黙の型変換を避ける:
const s = String(n) + x;
// ホットパスではクロージャ生成を避ける:
for (let i = 0; i < n; i++) { }
// map で毎回アロー関数を生成しない

メモリ監視

Node のメモリ:`process.memoryUsage()`、`--max-old-space-size`。ブラウザでは Performance API。

1
2
3
4
5
6
7
8
9
// Node:
console.log(process.memoryUsage());
// heapUsed 使用済みヒープ、heapTotal 総ヒープ
// ヒープを増やす:node --max-old-space-size=4096 app.js
// ブラウザ:
performance.measureMemory?.()
.then(m => console.log(m.bytes));
// ヒープスナップショット:Chrome DevTools の Memory パネル
// リークの特徴:heapUsed が下がり続けることなく増加

クロージャとメモリ

クロージャは外部変数を保持し寿命を延ばす。ループ内のクロージャ罠に注意。参照を解放する。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function makeCounter() {
let count = 0; // クロージャにキャプチャされる
return () => ++count; // 生存する
}
const c = makeCounter();
c(); // 1
// ループ内クロージャキャプチャ(var の罠):
// BAD:var は共有される
for (var i = 0; i < 3; i++) {
setTimeout(() => console.log(i)); // 3 3 3
}
// GOOD:let はブロックスコープでキャプチャ
for (let i = 0; i < 3; i++) {
setTimeout(() => console.log(i)); // 0 1 2
}
// 長寿命クロージャの解放:
// fn = null

TypedArray とバイナリ

TypedArray でバイナリ数値を処理:`Uint8Array` / `Float64Array`。ビューはバッファを共有。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const bytes = new Uint8Array(16); // 16 バイト
bytes[0] = 255;
// 既存データから作成:
const arr = new Uint8Array([1, 2, 3]);
// 浮動小数:
const floats = new Float64Array(8);
// バッファ共有:
const buffer = new ArrayBuffer(16);
const view = new DataView(buffer);
view.setInt32(0, 42);
view.getInt32(0); // 42
// 通常の配列に変換:
const plain = Array.from(bytes);
// エンコード:
new TextEncoder().encode('中文');
// 大容量ファイル/ネットワークプロトコル/Canvas ピクセルで頻出

10.クラスとオブジェクト指向

class、アクセス修飾子、継承、抽象クラス、ジェネリッククラス。

class の基本

class 構文:フィールド、コンストラクタ、メソッド。フィールドに型と可視性を注釈可能。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Person {
name: string;
age: number;
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
greet(): string {
return `Hi, I'm ${this.name}`;
}
}
const p = new Person('Nick', 30);
// パラメータプロパティ shorthand:
class P2 {
constructor(public name: string, private age: number) {}
}

アクセス修飾子

`public`、`private`、`protected`、`readonly`。すべてコンパイル時のチェック。

1
2
3
4
5
6
7
8
9
10
11
12
class Account {
public owner: string; // デフォルト public
private balance = 0; // プライベート
protected type = 'basic'; // サブクラスからアクセス可
readonly id: string; // 読み取り専用
constructor(owner: string) {
this.owner = owner;
this.id = crypto.randomUUID();
}
}
// # プライベートフィールド(実行時プライベート、ES2022):
class C { #secret = 1; get() { return this.#secret; } }

継承と override

`extends` で継承、`super()` で親コンストラクタ呼び出し、`override` でメソッド上書き。サブクラスは親(is-a)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Animal {
constructor(public name: string) {}
speak(): string { return `${this.name} makes a sound`; }
}
class Dog extends Animal {
constructor(name: string, public breed: string) {
super(name); // まず親コンストラクタを呼ぶ
}
override speak(): string { // override を明示
return `${this.name} barks`;
}
}
const d = new Dog('Rex', 'Husky');
// d は Dog であり Animal でもある

抽象クラスとインターフェース

abstract クラスはインスタンス化不可、抽象メソッドはサブクラスで実装必須。インターフェースは形状を制約。抽象クラスは実装を持てる。

1
2
3
4
5
6
7
8
9
10
11
12
13
abstract class Shape {
abstract area(): number; // サブクラスは必ず実装
describe(): string {
return `Area: ${this.area()}`;
}
}
class Circle extends Shape {
constructor(private r: number) { super(); }
override area(): number { return Math.PI * this.r ** 2; }
}
// interface 制約:
interface HasArea { area(): number }
function printArea(s: HasArea) { console.log(s.area()); }

implements

`class implements インターフェース`:クラスはインターフェースの形状を満たす必要がある。複数のインターフェースを実装可能。

1
2
3
4
5
6
7
8
9
10
11
12
interface Runnable {
run(): void;
}
interface Jumpable {
jump(): void;
}
class Player implements Runnable, Jumpable {
run(): void { console.log('running'); }
jump(): void { console.log('jump'); }
}
// メソッド不足はコンパイルエラー
// implements は形の制約のみ、継承関係を要求しない

ジェネリッククラス

ジェネリッククラス:型パラメータをフィールドとメソッドに使用。制約でジェネリックの範囲を絞る。

1
2
3
4
5
6
7
8
9
10
11
class Box<T> {
private value: T;
constructor(value: T) { this.value = value; }
get(): T { return this.value; }
set(v: T): void { this.value = v; }
}
const numBox = new Box<number>(42);
const strBox = new Box('hi'); // string と推論
// 静的メンバーは型パラメータを参照できない:
// static arr: T[] // エラー
// ジェネリック制約:class Box<T extends { id: number }>

getter / setter

`get` / `set` アクセサでフィールド読み書きをラップ。検証や計算ロジックを追加できる。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Temperature {
private _celsius = 0;
get celsius(): number { return this._celsius; }
set celsius(value: number) {
if (value < -273.15) throw new Error('绝对零度以下');
this._celsius = value;
}
get fahrenheit(): number {
return this._celsius * 9 / 5 + 32;
}
}
const t = new Temperature();
t.celsius = 25;
console.log(t.fahrenheit); // 77

静的メンバー

`static` でクラスレベルのフィールドとメソッドを定義。静的メンバーはインスタンスに依存しない。ファクトリや定数に `static`。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class MathHelper {
static readonly PI = 3.14159; // クラス定数
static max(arr: number[]): number {
return Math.max(...arr);
}
// ファクトリーメソッド:
static create(name: string): MathHelper {
return new MathHelper(name);
}
constructor(private name: string) {}
}
MathHelper.PI;
MathHelper.max([1, 5, 3]);
// 静的メンバーはクラス名でアクセス:
// new MathHelper().PI // エラー
// 静的プロパティ初期化はクラス定義時に実行される

11.例外処理

throw / try-catch、エラー型、独自エラー、非同期エラー。

throw / try-catch

`throw` でエラーを投げ、`try-catch` で捕捉、`finally` で後処理。`catch` の変数は既定で `unknown` なので絞り込みが必要。

1
2
3
4
5
6
7
8
9
10
11
try {
const n = JSON.parse(text);
if (typeof n !== 'number') throw new Error('需要数字');
} catch (err) {
if (err instanceof Error) {
console.log(err.message); // 絞り込み後にアクセス
}
} finally {
cleanup(); // 成否に関わらず実行
}
// キャッチしないと上位へ伝播、未処理だとクラッシュ

独自エラー

`Error` を extends して独自エラークラスを定義。追加情報を保持。エラー名で型を区別。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class ValidationError extends Error {
constructor(public field: string, message: string) {
super(message);
this.name = 'ValidationError';
}
}
try {
throw new ValidationError('email', '邮箱格式错误');
} catch (err) {
if (err instanceof ValidationError) {
console.log(err.field, err.message);
}
}
// プロトタイプチェーンを確実に(instanceof のため):
// Object.setPrototypeOf(this, ValidationError.prototype)

よくあるエラー型

Error 基底クラス:`TypeError` 型エラー、`RangeError` 範囲エラー、`ReferenceError` 参照エラー。`instanceof` で判定。

1
2
3
4
5
6
7
8
9
10
11
12
try {
// TypeError: 存在しないメソッド呼び出し
// RangeError: 配列範囲外 / 再帰が深すぎる
// ReferenceError: 未宣言変数の参照
const arr = [1, 2];
arr[5].toFixed(); // TypeError
} catch (err) {
if (err instanceof TypeError) { }
else if (err instanceof RangeError) { }
// instanceof Error でフォールバック
}
// クロス realm では name 属性で判定

非同期エラー

`async` 関数の `throw` は拒否された Promise に。`await` 時に `try-catch` で捕捉。`.catch` チェーンでも可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
async function load(): Promise<void> {
try {
const res = await fetch('/api');
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const data = await res.json();
} catch (err) {
console.error('加载失败', err);
}
}
// Promise チェーン:
fetch('/api').then(r => r.json()).catch(e => {
console.error(e);
});
// 未処理の rejected Promise は unhandledrejection をトリガ

エラーバウンダリ

モジュール境界でエラーを捕捉・変換。サードパーティのエラーは統一的にラップ。エラーの漏洩によるクラッシュを防ぐ。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function safeParse<T>(json: string): T {
try {
return JSON.parse(json) as T;
} catch {
throw new Error('JSON 解析失败'); // 統一的にラップ
}
}
// エントリでキャッチ:
process.on('uncaughtException', (err) => {
console.error('未捕获异常', err);
process.exit(1);
});
// Node では同期例外をトップレベルでフォールバック
// ブラウザ:window.onerror / unhandledrejection

エラー処理パターン

予測可能なエラーは結果を返し、予期しないものは例外を投げる。Result 風(ok / err)と `throw` にはそれぞれ使い所がある。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 予想可能な失敗:null / Result オブジェクトを返す
type Result<T> = { ok: true; value: T } | { ok: false; error: string };
function parseNum(s: string): Result<number> {
const n = Number(s);
return Number.isNaN(n)
? { ok: false, error: '不是数字' }
: { ok: true, value: n };
}
const r = parseNum('abc');
if (r.ok) console.log(r.value);
else console.log(r.error);
// 予想外の失敗:throw + 上位でキャッチ
// ルール:呼び出し側が処理できるなら返す、そうでなければ throw
// 握り潰さない:catch 後は少なくとも log

エラーメッセージの質

エラーメッセージはコンテキストを含む:何、どこ、どう修正。独自エラーはフィールドを保持。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class HttpError extends Error {
constructor(
public status: number,
public url: string,
message: string,
) {
super(message);
this.name = 'HttpError';
}
}
// GOOD:コンテキスト付き
throw new HttpError(404, url, `资源不存在: ${url}`);
// BAD:情報不足
// throw new Error('失败了');
// ログにスタックを含める:
console.error(err); // stack を保持
// エラー原因チェーン:
new Error('外层失败', { cause: innerErr })

未処理の拒否

未捕捉の rejected Promise は `unhandledrejection` を発火。最上位でフォールバックして静かな失敗を防ぐ。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// Node:
process.on('unhandledRejection', (reason) => {
console.error('未处理的 Promise 拒绝', reason);
});
// ブラウザ:
window.addEventListener('unhandledrejection', (e) => {
e.preventDefault();
console.error('未处理拒绝', e.reason);
});
// 同期例外:
process.on('uncaughtException', (err) => {
console.error('未捕获异常', err);
process.exit(1);
});
// 監査:テストでリスナーを張って取りこぼし検知
// フォールバックは握り潰しではなく、ログと特定

12.入出力

console 出力、Node のファイル / ストリーム、fetch ネットワーク、型付き JSON。

console 出力

`console.log` / `info` / `warn` / `error`、テンプレート出力、`%o` フォーマット、グループ化とカウント。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
console.log('Hello');
console.info('info 消息');
console.warn('警告');
console.error('错误');
// フォーマット:
console.log('%o', { a: 1 }); // オブジェクト展開
console.table([{ a: 1 }, { a: 2 }]);
// グループ:
console.group('组');
console.log('内容');
console.groupEnd();
// 時間計測:
console.time('t');
console.timeEnd('t');

fetch ネットワーク

`fetch` で非同期リクエスト。`await` で応答、JSON パース、エラー処理、結果の型アサーション。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
async function getUsers(): Promise<User[]> {
const res = await fetch('/api/users');
if (!res.ok) throw new Error(`HTTP ${res.status}`);
return res.json() as Promise<User[]>; // アサーション
}
// POST:
await fetch('/api', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name: 'Nick' }),
});
// AbortController でタイムアウト:
const ac = new AbortController();
setTimeout(() => ac.abort(), 3000);
await fetch(url, { signal: ac.signal });

Node のファイル

`fs/promises` で非同期読み書き。`readFile` / `writeFile` / `mkdir` / `readdir` は Promise を返す。

1
2
3
4
5
6
7
8
9
10
import { readFile, writeFile, mkdir, readdir } from 'node:fs/promises';
const text = await readFile('data.txt', 'utf8');
await writeFile('out.txt', text.toUpperCase());
await mkdir('dir', { recursive: true });
const files = await readdir('.');
// 大容量ファイルのストリーム:
import { createReadStream } from 'node:fs';
const stream = createReadStream('big.log');
// @types/node が必要:
// npm i -D @types/node

JSON と型

`JSON.parse` / `stringify` でシリアライズ。`parse` の結果はガードで検証、`stringify` は関数を無視。

1
2
3
4
5
6
7
8
9
10
11
12
13
interface User { name: string; age: number }
const u: User = { name: 'Nick', age: 30 };
const json = JSON.stringify(u);
// パース後にバリデーション:
function isUser(v: unknown): v is User {
return typeof v === 'object' && v !== null
&& typeof (v as any).name === 'string'
&& typeof (v as any).age === 'number';
}
const data = JSON.parse(json);
if (isUser(data)) console.log(data.name);
// JSON.parse は any を返すためアサート前にガード推奨
// シリアライズオプション:JSON.stringify(u, null, 2) で整形

ストリーム処理

`ReadableStream` / Web Streams で大きな応答を処理。読み込み進捗、チャンクごとの処理。

1
2
3
4
5
6
7
8
9
10
11
12
13
const res = await fetch('/big');
const reader = res.body!.getReader();
const decoder = new TextDecoder();
let total = 0;
while (true) {
const { done, value } = await reader.read();
if (done) break;
total += value.length;
const chunk = decoder.decode(value, { stream: true });
process(chunk);
}
// 大容量ファイルをストリーム読み込み:
// for await (const chunk of createReadStream('big.log')) { }

環境変数と引数

Node は `process.argv` / `process.env` を読み取る。引数解析、環境変数の型絞り込み。

1
2
3
4
5
6
7
8
9
10
11
// process.argv[0]=node, [1]=スクリプト, [2..] 引数
const args = process.argv.slice(2);
// 環境変数:
const port = Number(process.env.PORT ?? 3000);
const mode = process.env.NODE_ENV ?? 'development';
// 判定:
if (mode === 'production') { }
// .env 読み込み:
// npm i dotenv
// import 'dotenv/config'
// 型安全な環境:process.env は Record<string, string | undefined>

ブラウザ Web API

`localStorage` / `sessionStorage`、`navigator`、`WebSocket` の型。保存値はシリアライズが必要。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// ローカルストレージ:
localStorage.setItem('token', 'abc');
const t = localStorage.getItem('token'); // string | null
// オブジェクト保存にはシリアライズが必要:
localStorage.setItem('user', JSON.stringify(user));
// 読み戻しはパース + 検証:
const raw = localStorage.getItem('user');
if (raw) { const u = JSON.parse(raw) as User; }
// ナビゲーション:
if ('geolocation' in navigator) {
navigator.geolocation.getCurrentPosition((pos) => {
console.log(pos.coords.latitude);
});
}
// WebSocket イベント型:
ws.addEventListener('message', (e: MessageEvent) => {
console.log(e.data);
});

ファイル読み込み

`input[type=file]` で `File` を取得、`FileReader` でテキスト / DataURL 読み込み、オブジェクト URL でプレビュー。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const input = document.querySelector('input[type=file]') as HTMLInputElement;
input.addEventListener('change', async () => {
const file = input.files?.[0];
if (!file) return;
// テキスト読み込み:
const text = await file.text();
// DataURL として読み込み:
const reader = new FileReader();
reader.onload = () => console.log(reader.result);
reader.readAsDataURL(file);
// 画像プレビュー:
const url = URL.createObjectURL(file);
img.src = url;
// File には name/size/type がある
console.log(file.name, file.size, file.type);
});

13.よくある落とし穴

日常開発で踏みがちな落とし穴と正しい書き方。

any の濫用

`any` は型チェックをオフにし本物のエラーを隠す。可能なら `unknown` + ガードを使う。

1
2
3
4
5
6
7
8
9
10
// BAD:any がエラーを握り潰す
function getLen(v: any): number {
return v.length; // コンパイル通過、ランタイムでクラッシュの可能性
}
// GOOD:unknown + ガード
function getLen(v: unknown): number {
if (typeof v !== 'string') return 0;
return v.length;
}
// any の暗黙伝播:戻り値 any が呼び出し側を汚染

空値の判定

`!!` で真偽値判定、`== null` で null / undefined 両方判定、配列の空判定。`0` や `''` を空と扱わない。

1
2
3
4
5
6
7
8
9
10
// BAD:値が 0 や '' の可能性あり
if (value) { } // 0 は falsy
// GOOD:明示的に null を判定
if (value !== null && value !== undefined) { }
// 簡略 == null で両方判定:
if (value == null) { } // null または undefined
// 配列が空の判定:
if (arr.length === 0) { }
// オブジェクトが空の判定:
if (Object.keys(obj).length === 0) { }

async の誤用

async 関数は必ず Promise を返す。`await` を忘れると Promise が漏れる。`forEach` は async を待たない。

1
2
3
4
5
6
7
8
9
10
11
12
13
// BAD:forEach は待たない
async function main() {
[1, 2, 3].forEach(async n => { await work(n); });
console.log('done'); // 先に表示される!
}
// GOOD:for...of
for (const n of [1, 2, 3]) {
await work(n);
}
// BAD:await を忘れる
const res = fetch('/api'); // Promise のまま
// 並列:
await Promise.all([a(), b()]);

等価比較

`===` は厳密等価。`NaN !== NaN`。オブジェクトは参照比較。深い比較は手書きまたはライブラリ。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// BAD:== の緩い比較は型変換される
'1' == 1; // true、落とし穴
// GOOD:=== で厳密比較
'1' === 1; // false
// NaN は特殊:
NaN === NaN; // false
Number.isNaN(NaN); // true
// オブジェクトは参照で比較:
{} === {}; // false
// 配列内容の比較:
[1, 2].join() === [1, 2].join(); // 簡易版
// 深い比較ライブラリ:
// import { isEqual } from 'lodash-es';
// パフォーマンス:深いオブジェクトの頻繁な比較を避ける

null と undefined の混同

`null` は意図的な空値、`undefined` は未代入。strictNullChecks 下では別々に処理。オプショナルチェイニングとアサーションを使い分け。

1
2
3
4
5
6
7
8
9
10
11
12
// BAD:アサーションが null を見えなくする
const len = name!.length; // name が実際に null かもしれない
// GOOD:先に判定
if (name) {
const len = name.length;
}
// null 合体:
const n = name ?? 'default';
// オプショナルチェーンで安全アクセス:
user?.profile?.email;
// ?? を使う(|| ではなく):0 や '' は有効値
const port = port ?? 3000; // port=0 のとき 0 を保持

this の喪失

コールバック内で `this` が `undefined` になる。アロー関数または明示的 `.bind`。クラスフィールドのアロー関数が一般的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Counter {
count = 0;
increment = () => { this.count++; }; // アローで束縛
}
// BAD:コールバックで this が失われる
class C {
value = 1;
method() { return this.value; }
}
const fn = new C().method;
fn(); // undefined エラー
// GOOD:
const fn2 = new C().method.bind(new C());
// または呼び出し時に .method() 形式で

絞り込みの崩壊

プロパティアクセス後は絞り込みが失われる。先に変数に取り出してから絞り込む。引数を再代入すると広がる。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// BAD:プロパティの絞り込みが保たれない
function f(p: { a?: string }) {
if (p.a) {
p.a.toUpperCase(); // 既に変化している可能性
}
}
// GOOD:一旦変数に保存
function f2(p: { a?: string }) {
const a = p.a;
if (a) {
a.toUpperCase();
}
}
// オプショナルプロパティを 2 回参照すると競合の可能性
// 引数を再代入すると絞り込みが失われる:
// let x = v; その後判定

網羅性チェック

判別共用の `default` で `never` を使い網羅性をチェック。新しい分岐を追加すると未処理はコンパイルエラー。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// GOOD:default で never を使い網羅性チェック
type Event =
| { kind: 'open' }
| { kind: 'close' };
function handle(e: Event) {
switch (e.kind) {
case 'open': break;
case 'close': break;
default: {
const _exhaustive: never = e;
// 新しい kind 追加時ここでコンパイルエラー
return _exhaustive;
}
}
}
// BAD:default を付けない、新規型は黙って未処理
// 型を更新するとコンパイラが全 switch をチェック

コピーと変更

配列 / オブジェクトは参照共有のため誤変更に注意。更新前にコピー。`sort` はその場変更。

1
2
3
4
5
6
7
8
9
10
11
12
13
const a = [1, 2, 3];
// BAD:sort は in-place
const sorted = a.sort(); // a も変わる
// GOOD:先にコピー
const sorted = [...a].sort();
// BAD:参照共有による誤変更
const b = a;
b.push(4); // a も変わる
// シャローコピー:
const copy = [...a];
// オブジェクトスプレッドでシャローコピー:
const o2 = { ...o1 };
// ネストした深いコピーは層ごとに処理が必要

14.並行処理と非同期

Promise、async / await、Worker スレッド、イベントループ。

Promise

Promise は非同期結果を表す。`then` チェーン、`catch` でエラー、`finally` で後処理。型注釈は `Promise<T>`。

1
2
3
4
5
6
7
8
9
10
const p: Promise<number> = new Promise((resolve, reject) => {
setTimeout(() => resolve(42), 1000);
});
p.then(n => console.log(n))
.catch(err => console.error(err))
.finally(() => console.log('done'));
// 即時作成:
Promise.resolve(1);
Promise.reject(new Error('x'));
// ジェネリクス:Promise<T> の成功値型は T

async / await

async 関数は Promise を返す。`await` で展開。エラーは `try-catch`。トップレベル `await` は ESM が必要。

1
2
3
4
5
6
7
8
9
10
11
12
async function load(): Promise<User> {
const res = await fetch('/api/user');
if (!res.ok) throw new Error('加载失败');
return res.json() as Promise<User>;
}
// 呼び出し:
const user = await load();
// エラー:
try { await load(); } catch (err) { }
// 並列実行:
const [a, b] = await Promise.all([load(), load()]);
// トップレベル await(ESM .mjs)

並列と競合

`Promise.all` は全部完了、`allSettled` は全部(失敗含む)、`race` は最初に完了、`any` は最初に成功。

1
2
3
4
5
6
7
8
9
10
11
const tasks = [fetch1(), fetch2(), fetch3()];
// 全部成功(1 つでも失敗で全体失敗):
const all = await Promise.all(tasks);
// 中断せず全結果を収集:
const settled = await Promise.allSettled(tasks);
// 最初に完了したもの(失敗含む):
const first = await Promise.race(tasks);
// 最初に成功したもの(全部失敗で AggregateError):
const any = await Promise.any(tasks);
// 並列数制限:
// for ループで分割、または p-limit ライブラリ

Worker スレッド

Web Worker / Node `worker_threads` で並列計算。`postMessage` で通信、`transferable` で所有権移転。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// メインスレッド:
const worker = new Worker('/worker.js');
worker.postMessage({ n: 42 });
worker.onmessage = (e) => console.log(e.data);
worker.onerror = (e) => console.error(e);
// worker.js:
self.onmessage = (e) => {
const result = heavyCompute(e.data.n);
self.postMessage(result);
};
// Node:
// import { Worker } from 'node:worker_threads';
// 大きなオブジェクトは transfer:
// postMessage(buf, [buf.buffer])

イベントループ

同期コードが先に実行、マイクロタスク(Promise)がマクロタスク(setTimeout)より先。ブロッキングは全体を停滞させる。

1
2
3
4
5
6
7
8
9
console.log('1'); // 同期
Promise.resolve().then(() =>
console.log('2')); // マイクロタスク
setTimeout(() => console.log('3'), 0); // マクロタスク
console.log('4');
// 出力順:1 4 2 3
// 長時間タスクがイベントループをブロック:
// for (let i=0;i<1e9;i++){} // 固まる
// 分割または譲歩:await new Promise(r => setTimeout(r, 0))

ジェネレータ

`function*` ジェネレータは遅延生成。`yield` で一時停止・再開。型注釈は `Generator`。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function* count(max: number): Generator<number> {
let i = 0;
while (i < max) {
yield i++; // 一時停止して値を返す
}
}
for (const n of count(3)) console.log(n); // 0 1 2
// 手動で進める:
const g = count(2);
g.next(); // { value: 0, done: false }
g.next();
// 無限シーケンス + 遅延評価:
function* naturals(): Generator<number> {
let n = 0;
while (true) yield n++;
}
// イテレータプロトコルと等価

非同期イテレータ

`for await...of` で非同期データソースを反復。非同期ジェネレータ `async function*`。ストリームデータの消費。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
async function* generate(): AsyncGenerator<number> {
let i = 0;
while (i < 3) {
await new Promise(r => setTimeout(r, 100));
yield i++;
}
}
// 消費:
for await (const n of generate()) {
console.log(n); // 0 1 2
}
// 非同期イテラブルの利用例:
const res = await fetch('/big');
const reader = res.body!.getReader();
// Node ストリーム:
// for await (const chunk of createReadStream('f'))
// 遅延 + バックプレッシャー、メモリに優しい

並行数制限

同時実行される非同期タスク数を制御。バッチ、セマフォ、`p-limit` などの手法でリソース枯渇を防ぐ。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
async function mapLimit<T, R>(
items: T[], limit: number, fn: (x: T) => Promise<R>,
): Promise<R[]> {
const results: R[] = [];
let i = 0;
const workers = Array.from({ length: Math.min(limit, items.length) },
async () => {
while (i < items.length) {
const idx = i++;
results[idx] = await fn(items[idx]);
}
});
await Promise.all(workers);
return results;
}
// 用途:分割ダウンロード/リクエスト、limit で並列度を制御
// 一括送信を避けたいバッチ処理など

15.ネットワークとモジュール

モジュールシステム、import / export、fetch、型付き API ラッパー。

ES モジュール

`import` / `export` で静的にインポート / エクスポート。TS 型のエクスポートは `export type`。モジュールはスコープを隔離。

1
2
3
4
5
6
7
8
9
10
11
// utils.ts:
export const version = '1.0';
export function add(a: number, b: number): number { return a + b; }
export type ID = string; // 型のエクスポート
export default class App { } // デフォルトエクスポート
// main.ts:
import App, { add, version, type ID } from './utils';
// リネーム:
import { add as plus } from './utils';
// 全体インポート:
import * as utils from './utils';

型のみのインポート

`import type` は型のみを取り込みコンパイル時に消去。実行時依存や循環参照を避ける。

1
2
3
4
5
6
7
8
9
10
11
// BAD:型がランタイムに残る(esbuild で残ることがある)
// GOOD:
import type { User } from './types';
// ミックス:
import { fetchUsers, type User } from './api';
// 型のみ:
import type { Options } from './config';
// 型のエクスポート:
export type { ID } from './utils';
// コンパイラが自動で elide、明示するとより明確
// 循環参照時は import type で断ち切る

型付き API のラッパー

`fetch` をラップして強い型を返す。レスポンス検証、エラー統一処理。ジェネリックなリクエスト関数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
async function api<T>(
url: string, opts?: RequestInit
): Promise<T> {
const res = await fetch(url, opts);
if (!res.ok) {
throw new Error(`HTTP ${res.status}`);
}
return res.json() as Promise<T>;
}
// 使用例:
interface User { name: string }
const user = await api<User>('/api/user');
// ジェネリクスで呼び出し側が型安全に
// 境界では schema バリデーション(zod)で実行時のズレを防ぐ
// import { z } from 'zod';

Node HTTP サーバ

`node:http` またはフレームワーク(Express / Fastify)。型付きリクエスト / レスポンス。ルーティング。

1
2
3
4
5
6
7
8
9
10
11
import { createServer } from 'node:http';
const server = createServer((req, res) => {
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ ok: true }));
});
server.listen(3000);
// Express:
// import express from 'express';
// @types/express が型を提供
// パラメータの型:
// app.get('/user/:id', (req: Request<{id: string}>, res: Response) => {})

DOM の型

`document.getElementById` の戻り型、イベント型、HTML 要素の型マッピング。

1
2
3
4
5
6
7
8
9
10
const btn = document.getElementById('btn');
// HTMLElement | null、非 null チェックが必要:
if (btn) { btn.addEventListener('click', handler); }
// 具体要素にアサート:
const input = document.querySelector('input') as HTMLInputElement;
// イベント型:
function handler(e: MouseEvent) { console.log(e.clientX); }
// ジェネリックイベント:
const f = (e: KeyboardEvent) => { e.key };
// フォームの値取得:input.value は string

URL とパラメータ

`URLSearchParams` でクエリ文字列を構築、`URL` でパース。型安全にパラメータを読み取る。

1
2
3
4
5
6
7
8
9
const params = new URLSearchParams({ q: 'ts', page: '2' });
params.toString(); // 'q=ts&page=2'
const url = new URL('https://example.com/search?q=ts');
const q = url.searchParams.get('q'); // 'ts'
// 組み立て:
url.searchParams.set('page', '3');
// リクエストヘッダ:
headers.append('Authorization', `Bearer ${token}`);
// パラメータ型:get は string | null を返すため絞り込んで使う

WebSocket

WebSocket は双方向通信。`onmessage` イベント、ジェネリックなデータ、`readyState`。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const ws = new WebSocket('wss://example.com/socket');
// 接続確立:
ws.onopen = () => ws.send(JSON.stringify({ type: 'join' }));
// 受信:
ws.onmessage = (e: MessageEvent) => {
// e.data は string | Blob | ArrayBuffer の可能性
const data = JSON.parse(e.data as string);
console.log(data);
};
ws.onerror = (e) => console.error('连接错误', e);
ws.onclose = (e) => console.log('关闭', e.code);
// 能動的に閉じる:
ws.close(1000, '正常关闭');
// 自動再接続:onclose 内で setTimeout 再接続

ヘッダと認証

型安全に `Headers` を設定。`Authorization: Bearer ...`、`Content-Type`。インターセプタで統一注入。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
const headers = new Headers();
headers.set('Content-Type', 'application/json');
headers.set('Authorization', `Bearer ${token}`);
const res = await fetch('/api', { headers });
// レスポンスヘッダ読み取り:
const type = res.headers.get('content-type');
// 共通化ラッパー:
function authFetch(url: string, init?: RequestInit) {
return fetch(url, {
...init,
headers: {
...init?.headers,
Authorization: `Bearer ${getToken()}`,
},
});
}
// 注意:token 漏洩防止のためログ/URL に含めない
// リフレッシュ:401 時に token を更新して再試行

16.日付と時刻

Date オブジェクト、タイムスタンプ、フォーマット、タイムゾーン。

Date の基本

`Date` のコンストラクタ、`getFullYear` / `getMonth` / `getDate` の読み出し、`set*` の設定。月は 0 始まり。

1
2
3
4
5
6
7
8
9
10
11
const now = new Date();
const d = new Date('2026-08-02T12:00:00Z');
const y = d.getFullYear(); // 2026
const m = d.getMonth(); // 7(0 から!)
const day = d.getDate(); // 2
// 設定:
d.setFullYear(2030);
d.setMonth(0); // 1 月
// ローカルと UTC:
// getUTCFullYear() / getUTCHours()
// getTimezoneOffset() 分差

タイムスタンプ

`getTime()` ミリ秒タイムスタンプ、`Date.now()`、`Date.parse`。日付の比較はタイムスタンプで。

1
2
3
4
5
6
7
8
9
const ms = Date.now(); // 現在のミリ秒
const d = new Date(ms);
const older = new Date('2020-01-01');
if (d.getTime() > older.getTime()) { } // 比較
// 秒単位:Math.floor(Date.now() / 1000)
// Date.parse('2026-08-02') はミリ秒を返す
// 日時の加算:
d.setDate(d.getDate() + 7); // 7 日加算
// setMonth/setDate は月跨ぎを自動処理

フォーマット

`toISOString` は UTC 形式、`toLocaleDateString` はローカル形式、`Intl.DateTimeFormat` でカスタム。

1
2
3
4
5
6
7
8
9
10
const d = new Date();
const iso = d.toISOString(); // '2026-08-02T04:00:00.000Z'
const local = d.toLocaleDateString('zh-CN');
// Intl でカスタム:
new Intl.DateTimeFormat('zh-CN', {
year: 'numeric', month: 'long', day: 'numeric',
hour: '2-digit', minute: '2-digit',
}).format(d);
// 相対時間:
// d.toLocaleTimeString('zh-CN')

タイムゾーン

タイムスタンプは UTC ミリ秒で、フォーマットして初めてローカルタイムゾーンが反映される。`Intl` の `timeZone` オプションで指定。

1
2
3
4
5
6
7
8
9
10
11
const d = new Date();
// タイムゾーン指定:
new Intl.DateTimeFormat('zh-CN', {
timeZone: 'Asia/Shanghai',
hour12: false,
}).format(d);
// getTimezoneOffset はローカルと UTC の分差を返す
// 保存は ISO/タイムスタンプ、表示はローカライズ
// タイムゾーン跨ぎ変換:
// UTC で保存、読み出し時に toLocaleString('zh-CN', { timeZone })
// 単純計算は dayjs/date-fns ライブラリ

タイマ

`setTimeout` で遅延、`setInterval` で周期、`clearTimeout` でキャンセル。戻り値の型は `number`。

1
2
3
4
5
6
7
8
9
10
11
const timer = setTimeout(() => {
console.log('1s 后');
}, 1000);
clearTimeout(timer); // キャンセル
const interval = setInterval(() => {
console.log('每秒');
}, 1000);
clearInterval(interval); // 停止
// 非同期待機:
await new Promise(r => setTimeout(r, 500));
// 注意:タイマーコールバックはイベントループのマクロタスクで実行される

経過時間と区間

2 つのタイムスタンプの差がミリ秒単位の経過時間。区間判定はタイムスタンプで比較。`setInterval` のドリフトに注意。

1
2
3
4
5
6
7
8
9
10
11
const start = Date.now();
// 経過時間計測:
const elapsed = Date.now() - start; // ミリ秒
console.log(`${elapsed}ms`);
// 区間判定:
const inWindow = t >= start && t <= end;
// N ミリ秒ごと(ポーリング):
const poll = setInterval(() => { }, 1000);
// setInterval のドリフト補正:
// setTimeout 再帰 + 補正計算
// 精度:performance.now() の方が高い

日付ライブラリ

dayjs / date-fns は明快な API とタイムゾーン処理を提供。小型で不変。複雑なタイムゾーンはライブラリの使用を推奨。

1
2
3
4
5
6
7
8
9
10
11
12
13
// dayjs:
// import dayjs from 'dayjs';
// dayjs().format('YYYY-MM-DD');
// dayjs().add(7, 'day').toDate();
// dayjs('2026-08-02').isBefore('2026-09-01');
// date-fns:
// import { format, addDays, isBefore } from 'date-fns';
// format(new Date(), 'yyyy-MM-dd');
// addDays(new Date(), 7);
// タイムゾーン:
// import { formatInTimeZone } from 'date-fns-tz';
// formatInTimeZone(d, 'Asia/Shanghai', 'yyyy-MM-dd HH:mm')
// いずれもイミュータブル:新しい値を返し元の Date は変更しない

高精度タイマ

`performance.now()` はミリ秒精度で、システム時刻変更の影響を受けない。パフォーマンス計測に使用。

1
2
3
4
5
6
7
8
9
10
11
12
13
const t0 = performance.now();
// コードを計測…
const elapsed = performance.now() - t0;
console.log(`${elapsed.toFixed(2)}ms`);
// 壁時計時間:Date.now() は変更され得る
// performance.now() は単調増加
// ブラウザ/Node 両方で利用可能
// マーカー分析:
performance.mark('start');
// …
performance.mark('end');
performance.measure('任务', 'start', 'end');
// 計測結果:performance.getEntriesByName('タスク')

17.プロセスとシステム

Node プロセス、コマンドラインツール、標準ストリーム、ビルド成果物の実行。

Node プロセス

`process` グローバル:`argv` 引数、`env` 環境、`exit` コード、`stdout` / `stderr` ストリーム。

1
2
3
4
5
6
7
8
9
10
import { argv, env, exit, stdout, stderr } from 'node:process';
const args = argv.slice(2);
const mode = env.NODE_ENV;
exit(0); // 正常終了
stdout.write('输出');
stderr.write('错误');
// 終了コード:0 成功、1 一般エラー
process.exitCode = 1; // 優雅に設定
// シグナル処理:
process.on('SIGINT', () => { console.log('Ctrl+C'); process.exit(0); });

CLI ツール

CLI を作成:引数解析、ヘルプ出力、終了コード。shebang でスクリプトを実行可能に。

1
2
3
4
5
6
7
8
9
10
11
12
13
#!/usr/bin/env node
const [cmd, ...rest] = process.argv.slice(2);
if (cmd === '--help' || cmd === '-h') {
console.log('用法: ts-tool <命令> [选项]');
process.exit(0);
}
if (!cmd) {
console.error('缺少命令');
process.exit(1);
}
// 引数パースライブラリ:commander / yargs
// オプション:
// ts-tool build --out dist --watch

標準ストリーム

`stdin` で入力、`stdout` で出力、`stderr` でエラー。`readline` で対話。パイプデータ。

1
2
3
4
5
6
7
8
9
10
11
import { stdin, stdout } from 'node:process';
import * as readline from 'node:readline/promises';
const rl = readline.createInterface({ input: stdin, output: stdout });
const name = await rl.question('名字? ');
console.log(`你好, ${name}`);
rl.close();
// 全 stdin を読み込み:
import { readFileSync } from 'node:fs';
// パイプ:echo hi | ts-tool
// 行ごと処理:
for await (const line of rl) { process(line); }

コンパイル成果物の実行

tsc 後 `node dist/main.js` で実行。`package.json` の `bin` でコマンド登録。型宣言は `.d.ts`。

1
2
3
4
5
6
7
8
9
10
11
// package.json:
// {
// "bin": { "ts-tool": "./dist/cli.js" },
// "types": "./dist/index.d.ts",
// "main": "./dist/index.js"
// }
// コンパイル:npx tsc
// 実行:node dist/cli.js
// npm 公開:npm publish
// 宣言ファイル .d.ts を他 TS プロジェクトが参照可能
// tsconfig declaration: true

システムコマンドの実行

`execFile` で外部コマンド実行、`spawn` でストリーム。`child_process` の型。インジェクション回避のためエスケープに注意。

1
2
3
4
5
6
7
8
9
10
11
import { execFile } from 'node:child_process';
execFile('ls', ['-l'], (err, stdout) => {
if (err) { console.error(err); return; }
console.log(stdout);
});
// ストリーミング spawn:
import { spawn } from 'node:child_process';
const child = spawn('node', ['worker.js']);
child.stdout.on('data', (d) => console.log(d.toString()));
// セキュリティ:exec + 文字列連結は避ける
// 引数は配列で渡し、シェルコマンドを連結しない

終了コード

`0` 成功、非 0 失敗。慣例:`1` は一般エラー、`2` は使用法エラー。CI スクリプトは終了コードに依存。

1
2
3
4
5
6
7
8
9
process.exit(0); // 成功
process.exit(1); // 一般エラー
process.exit(2); // 使用法/引数エラー
// 未捕捉例外は終了コード 1
// 手動設定:
process.exitCode = 2;
// CI で終了コードが 0 以外は失敗:
// ts-tool check && echo OK || echo FAIL
// シグナル終了:SIGTERM はデフォルト 143

npm scripts

`package.json` の `scripts` でコマンドを編成。`pre` / `post` フック、`&&` 連結、`&` 並列。ビルドチェーンでよく使用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// package.json:
// {
// "scripts": {
// "dev": "tsx src/dev.ts",
// "typecheck": "tsc --noEmit",
// "lint": "eslint src",
// "test": "vitest run",
// "build": "npm run typecheck && tsup src/index.ts",
// "prepublishOnly": "npm run build"
// }
// }
// 連結:&&(失敗で中断)
// 並列:& または concurrently ライブラリ
// pre/post フック:prebuild は build 前に自動実行
// npx で一時実行:npx vitest

設定と環境

`dotenv` で `.env` を読み込み、型付き設定オブジェクト、実行時検証。設定とコードを分離。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// インストール:npm i dotenv
// import 'dotenv/config';
const port = Number(process.env.PORT ?? 3000);
const dbUrl = process.env.DATABASE_URL;
if (!dbUrl) {
throw new Error('缺少 DATABASE_URL');
}
// 型付き設定:
interface Config {
port: number;
debug: boolean;
apiKey: string;
}
// バリデーション関数が Config を返し、型を保証
// .env はバージョン管理外(gitignore)
// 環境差分:.env.development / .env.production

18.正規表現とテキスト処理

正規表現の構文、フラグ、型付きマッチ、テキスト処理の慣用句。

正規表現の構文

リテラル `/.../` または `RegExp` コンストラクタ。文字クラス、量指定子、グループ、 anchor。

1
2
3
4
5
6
7
8
9
const re = /\b\w+@\w+\.com\b/;
// \d 数字 \w 単語 \s 空白 \b 単語境界
// [abc] 文字クラス [^abc] 否定
// * 0 回以上 + 1 回以上 ? 0 または 1 {2,4} 区間
// ^ 先頭 $ 末尾
// () グループ (?:) 非キャプチャ
// または:/cat|dog/
// テスト:
re.test('hi [email protected]'); // true

フラグ

`g` グローバル、`i` 大文字小文字無視、`m` 複数行、`s` ドットが改行に一致、`u` Unicode、`y` スティッキー。

1
2
3
4
5
6
7
8
9
10
const g = /a/g; // グローバル(matchAll に必要)
const i = /HELLO/i; // 大文字小文字無視
const m = /^line/m; // 各行のアンカー
const s = /a.b/s; // . が改行にもマッチ
const u = /\p{Emoji}/u; // unicode プロパティ
const y = /a/y; // スティッキー(lastIndex から)
// 組み合わせ:/foo/gim
// 動的構築:
new RegExp(`\\d{${len}}`, 'gi');
// リテラルで \ はエスケープ必須

マッチと抽出

`match` は配列を返し(0 番目は全体一致 + キャプチャグループ)、`matchAll` はグローバルに反復、`match` 失敗時は `null` なのでチェックが必要。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const s = 'id=123&id=456';
const re = /id=(\d+)/g;
// 全件マッチ:
for (const m of s.matchAll(re)) {
console.log(m[1]); // '123' '456'
}
// 単発:
const first = s.match(re);
if (first) { console.log(first[0]); }
// 名前付きキャプチャ:
const named = /(?<year>\d{4})-(?<month>\d{2})/;
const r = s.match(named);
if (r) { r.groups!.year; }
// 最小マッチ ?:/(\d+?)(x)/ で最少マッチ

置換

`replace` は文字列 / 関数で置換。`$1` でキャプチャ参照、グローバル `g` で全置換。関数置換でロジックを処理。

1
2
3
4
5
6
7
8
9
10
11
const s = '2026-08-02';
// キャプチャ参照:
const a = s.replace(/(\d{4})-(\d{2})-(\d{2})/, '$3/$2/$1');
// 関数で置換:
const b = s.replace(/(\d+)/g, (m) => String(Number(m) + 1));
// 全置換(g 必須):
'aaa'.replace(/a/g, 'b'); // 'bbb'
// 空白除去:
s.replace(/\s+/g, ' ').trim();
// 単純置換は split/join も可:
s.split('-').join('/');

バリデーションの慣用句

全体一致は `^...$` で anchor。数値 / メール / URL のよく使うパターン。`test` は boolean を返す。

1
2
3
4
5
6
7
8
9
10
11
12
13
function isEmail(v: string): boolean {
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(v);
}
function isInt(v: string): boolean {
return /^[-+]?\d+$/.test(v);
}
function isUrl(v: string): boolean {
try { new URL(v); return true; }
catch { return false; }
}
// 厳密一致は ^ で始まり $ で終わる必須
// 長さチェック:/^.{8,20}$/
// 貪欲を回避:/^\.$/ でドットをマッチ

正規表現のパフォーマンス

破滅的バックトラックを避ける:ネストした量詞。正規表現をプリコンパイル。`g` の `lastIndex` 状態に注意。

1
2
3
4
5
6
7
8
9
10
11
// BAD:壊滅的バックトラック
// /^(a+)+$/ が 'aaaaaaaaaaaaaaaaaaaa!' にマッチするのは極めて遅い
// GOOD:ネストした量詞を避ける
/^(a+)$/;
// 事前コンパイルで再生成を避ける:
const re = /\d+/g; // モジュールレベルで再利用
// g フラグは状態を持つ:
re.lastIndex = 0; // リセット
// 大きなテキストはチャンク処理
// 単純パースは indexOf / split を優先
// 正規表現は構造マッチのみ、ビジネスロジックはコードで

正規表現の落とし穴

リテラルはエスケープが必要、`g` フラグの `lastIndex` 状態、貪欲マッチ、文字列内の `\` の二重エスケープ。

1
2
3
4
5
6
7
8
9
10
11
12
13
// 文字列構築時 \ は 2 回書く:
new RegExp('\\d+'); // /\d+/ と同等
// g フラグは lastIndex 状態を持つ:
const re = /a/g;
re.lastIndex = 0; // 使用前にリセット
// 貪欲マッチ:
'<a><b>'.match(/<.*>/); // 最後の > までマッチ
'<a><b>'.match(/<.*?>/); // 非貪欲、最短
// リテラル . はエスケープ:
/1\.0/; // '1.0' にマッチ、'1X0' ではない
// [] 内の ^ は否定:
/[^a]/; // a 以外
// 空マッチ:/(?:)/ は任意の位置にマッチ

よく使うパターン

ID / 電話番号 / 色 / 日付など、よく使う正規表現スニペット。業務フォーマットの検証用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 携帯番号(中国大陸):
const mobile = /^1[3-9]\d{9}$/;
// メール:
const email = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
// 16 進数カラー:
const color = /^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/;
// 日付 yyyy-mm-dd:
const date = /^\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])$/;
// URL:
const url = /^https?:\/\/[^\s]+$/;
// 中文字符:
const zh = /[\u4e00-\u9fff]/;
// 空行:
const blank = /^\s*$/;
// 記憶要点:^$ アンカー、数量限定、文字クラス

19.ビルドとツールチェーン

tsconfig、バンドラ、Lint / フォーマッタ、テスト、CI。

tsconfig 詳解

よく使うコンパイラオプション:`moduleResolution`、`declaration`、`noUnusedLocals`、`esModuleInterop`、`paths` エイリアス。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
{
"compilerOptions": {
"target": "ES2022",
"module": "ESNext",
"moduleResolution": "bundler",
"lib": ["ES2022", "DOM"],
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"declaration": true, // .d.ts を生成
"outDir": "dist",
"noUnusedLocals": true,
"paths": { "@/*": ["./src/*"] },
"resolveJsonModule": true
},
"include": ["src"]
}
// npm-run: tsc --noEmit で型チェック

バンドラ

Vite / Webpack / Rollup でバンドル。Vite は TS / フロントエンドのデフォルト。ライブラリは tsup / Rollup で ESM + CJS を出力。

1
2
3
4
5
6
7
8
9
10
11
12
// Vite:dev サーバー + ビルド
// vite.config.ts:
export default {
build: { target: 'esnext' },
// plugins: [react(), vue()]
};
// コマンド:
// npm run dev 開発
// npm run build ビルド
// ライブラリビルド:tsup
// tsup src/index.ts --format esm,cjs --dts
// 環境変数:import.meta.env.VITE_XXX

Lint とフォーマッタ

ESLint でルールチェック、Prettier でフォーマット。`ts-eslint` が型認識ルールを提供。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// ESLint 設定:
// {
// "parser": "@typescript-eslint/parser",
// "plugins": ["@typescript-eslint"],
// "rules": {
// "@typescript-eslint/no-explicit-any": "warn"
// }
// }
// コマンド:
// npx eslint src --fix
// Prettier:
// npx prettier --write "src/**/*.ts"
// ルールテンプレート:
// npx eslint --init
// コミット前:husky + lint-staged

テスト

Vitest / Jest でユニットテスト。`describe` / `it` / `expect`。型と実行は分離。TS を直接テスト。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import { describe, it, expect } from 'vitest';
import { add } from './add';
describe('add', () => {
it('两数相加', () => {
expect(add(1, 2)).toBe(3);
});
it('类型错误编译失败', () => {
// add('a', 2) // コンパイル時にブロック
});
});
// 型テスト:
// import { expectType } from 'tsd';
// カバレッジ:vitest run --coverage
// Mock:vi.mock() / vi.fn()

CI とデプロイ

CI ステージ:型チェック、Lint、テスト、ビルド。`tsc --noEmit` で型エラーをブロック。

1
2
3
4
5
6
7
8
9
10
11
// GitHub Actions:
// steps:
// - run: npm ci
// - run: npx tsc --noEmit # 型チェック
// - run: npx eslint src
// - run: npm test
// - run: npm run build
// - run: npm publish --dry-run
// 型チェックを最初に:失敗ですぐに中断
// キャッシュ:actions/cache で依存を高速化
// マルチバージョン:node 18/20/22 マトリクス

デバッグ

sourceMap + Node `--inspect` でブレークポイントデバッグ。`console` デバッグ、型アサーションで補助。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// tsconfig sourceMap: true
// VS Code launch.json:
// {
// "type": "node",
// "request": "launch",
// "program": "${file}",
// "runtimeArgs": ["--loader", "tsx"]
// }
// コマンド:
// node --inspect dist/main.js
// ブラウザ:DevTools Sources + sourcemap
// 型のデバッグ:
// type Debug<T> = T; hover で確認
// console 出力で位置特定をサポート

npm 公開

ライブラリ公開:`files` で公開内容、`version` はセマンティックバージョニング、`main` / `types` / `exports` エントリ。公開前にビルド。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// package.json:
// {
// "name": "@org/my-lib",
// "version": "1.2.0",
// "main": "./dist/index.js",
// "types": "./dist/index.d.ts",
// "exports": {
// ".": { "types": "./dist/index.d.ts", "import": "./dist/index.mjs" }
// },
// "files": ["dist"],
// "sideEffects": false
// }
// 公開:
// npm run build && npm publish
// ドライラン:npm publish --dry-run
// バージョン:npm version patch|minor|major
// 権限:npm publish --access public

monorepo 設定

npm / pnpm workspaces でマルチパッケージリポジトリ。依存共有、パッケージ間参照、スクリプト統一。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// pnpm-workspace.yaml:
// packages:
// - 'packages/*'
// - 'apps/*'
// npm workspaces:
// {
// "workspaces": ["packages/*"]
// }
// パッケージ間参照:
// npm i @org/shared -w packages/web
// ルートスクリプトで一括:
// pnpm -r run build
// 共通 TS 設定:
// tsconfig.base.json を各パッケージが extends
// 依存ホイスト:pnpm はデフォルト隔離、明示宣言が必要

公式リンク

公式ドキュメントとリソースへの直接リンク。

このチートシートについて

このページは TypeScript 5.x の自己完結型クイックリファレンスです。型システムと言語コアの、実プロジェクトの約 80% の一般的な使い方をカバーします。内容はモダンなイディオムに重点を置いています:インターフェースと型エイリアス、ジェネリック、共用 / 交差型、型の絞り込み、リテラル型、keyof / typeof、写像型と条件型、そして非同期プログラミングとの組み合わせ。TypeScript は 2012 年に Microsoft が公開した JavaScript のスーパーセットで、コンパイル時に JavaScript に静的型検査を追加し、大規模プロジェクトでの保守性を大幅に向上させます。現在、フロントエンドの主流の選択肢です。 19 のセクションはそれぞれ 1 つのテーマに焦点を当てています:基本構文、変数と型推論、型システム、参照と値セマンティクス、制御フロー、関数とオーバーロード、文字列とテンプレート、コレクション、メモリと型消去、クラスとインターフェース、エラー処理、入出力、よくある落とし穴、並行処理(非同期型)、ネットワーク、時間、プロセス、正規表現、ビルドツール(tsc / tsconfig)。各サブセクションには「概念紹介 + そのままコピーできるコードスニペット」が付属します。 すべてのコードとテキストはブラウザでローカルにレンダリングされ、データは一切デバイスから出ません。正確なリファレンスは TypeScript 公式ハンドブックを参照してください。

バージョン 2.1.0