一覧
型の書き方
-
アノテーション:
変数: 型- 値に型を宣言する
-
any/unknown:let x: unknown- 何でも入る型。
unknownは使う前に確認が必要
- 何でも入る型。
型を組み合わせる
-
ユニオン型:
A | B- 「AまたはB」を表す
-
リテラル型:
"値a" | "値b"- 「この値だけ」を表す
-
インターセクション型:
A & B- 「AかつB」を表す
型を絞り込む・上書きする
-
型ガード:条件分岐で型を絞り込む
typeof x === "型名"x instanceof クラス名"プロパティ名" in x-
function 関数名(x: unknown): x is 型(型述語)
-
never:(): never- 何も返さない・到達しないという型
-
アサーション:
値 as 型- 型を「こうだ」と上書きする
-
非 null アサーション:
値!-
null/undefinedではないと上書きする
-
型を後から指定する
-
ジェネリクス:
<T>- 型を後から指定できるようにする
値から型を取り出す
-
typeof/keyof:typeof 値/keyof 型- 値から型を、型からキーのユニオンを取り出す
-
as const:値 as const- 読み取り専用にして、型を最も狭くする
型を作る
-
インデックス型:
{ [key: string]: V }- 任意のキーに対する型を決める
-
マップ型:
{ [K in keyof T]: T[K] }- キーを順に回して型を作る
-
Record<K, V>- K が
stringならインデックス型、ユニオン型ならマップ型
- K が
-
条件型:
T extends A ? X : Y- 条件で型が変わる
-
infer:T extends F<infer U> ? U : never- 条件型の中で、型の一部を取り出す
-
ユーティリティ型:
Partial<T>Pick<T, K>Omit<T, K>など- よく使う変換の組み込み版
値の型を確かめる
-
satisfies:値 satisfies 型- 型を満たすことだけ確認し、実際の型は初期値のまま残す
アノテーション
const 変数: 型 = 値
function 関数名(引数: 型): 戻り値の型
変数や引数、戻り値に型を宣言する。
const age: number = 20;
function greet(name: string): string {
return `Hello, ${name}`;
}
any と unknown
let x: any
let x: unknown
any は、型チェックを無効にする型。何をしてもエラーにならない。
let a: any = "text";
a.foo(); // エラーにならない(実行時に落ちる)
unknown は、何でも入るが、使う前に型を確認させる型。
any の代わりに、まず unknown を使う。
let u: unknown = "text";
u.toUpperCase(); // エラー(型が分からない)
if (typeof u === "string") {
u.toUpperCase(); // OK(string に絞り込まれた)
}
ユニオン型
型A | 型B
「AまたはB」を表す型。| でつなぐ。
type Id = number | string;
const a: Id = 1;
const b: Id = "abc";
ユニオン型の値は、両方に共通する操作しかできない。
片方だけの操作をするには、型を絞り込む(後述の型ガード)。
function show(id: Id) {
id.toFixed(); // エラー(string には無い)
}
リテラル型
"値a" | "値b"
string ではなく、「この値だけ」という型。
ユニオン型と組み合わせると、選択肢を決められる。
type Mode = "light" | "dark";
const m: Mode = "light"; // OK
const n: Mode = "blue"; // エラー
数値や真偽値でも使える。
type Dice = 1 | 2 | 3 | 4 | 5 | 6;
インターセクション型
型A & 型B
「AかつB」を表す型。& でつなぐ。
両方のプロパティをすべて持つ型になる。
type Name = { name: string };
type Age = { age: number };
type Person = Name & Age;
// { name: string; age: number }
const p: Person = { name: "Taro", age: 20 }; // 両方必要
型ガード
typeof x === "型名"
x instanceof クラス名
"プロパティ名" in x
function 関数名(x: unknown): x is 型
条件分岐の中で、型を絞り込む。
ユニオン型や unknown を、安全に扱うために使う。
function show(id: number | string) {
if (typeof id === "string") {
id.toUpperCase(); // string
} else {
id.toFixed(); // number
}
}
絞り込みの書き方は、主に3つ。
| 書き方 | 絞り込めるもの |
|---|---|
typeof x === "string" |
プリミティブ型 |
x instanceof Date |
クラスのインスタンス |
"name" in x |
プロパティがあるオブジェクト |
独自の判定関数には、戻り値に x is 型 と書く(型述語)。
true を返したとき、呼び出し側で型が絞り込まれる。
function isString(x: unknown): x is string {
return typeof x === "string";
}
const v: unknown = "abc";
if (isString(v)) {
v.toUpperCase(); // string
}
never 型
function 関数名(): never
const x: never = 値
「値が存在しない」ことを表す型。
never には、どんな値も代入できない。
1. 必ず例外で止まる関数
呼び出し元に戻ってこない関数の戻り値になる。
void は「戻ってくるが値なし」、never は「戻ってこない」。
function fail(msg: string): never {
throw new Error(msg);
}
function f(x: string | null): string {
if (x === null) fail("null"); // ここで必ず止まる
return x; // string に絞り込まれている
}
2. 分岐の漏れをコンパイル時に見つける
条件分岐で型が絞り込まれ、全部処理し終わると、残りの型は never になる。
never に代入して、「残りがない」ことを確認する。
type Shape = "circle" | "square";
function area(s: Shape) {
if (s === "circle") return 1; // 残り: "square"
if (s === "square") return 2; // 残り: never
const _check: never = s; // OK(コンパイル時の検出)
throw new Error("未対応");
}
Shape に "triangle" を足すと、残りが "triangle" になり、エラーになる。
type Shape = "circle" | "square" | "triangle";
// const _check: never = s;
// エラー("triangle" は never に入らない)
-
throwだけだと、実行して初めて気づく -
neverへの代入なら、型を足した瞬間に気づく - 両方書くと、コンパイル時と実行時の両方で守れる
※ never は、条件型でも「この要素を消す」ために使う(条件型 で後述)。
アサーション
値 as 型
型を「こうだ」と上書きする。
まず型ガードで絞り込めないか考え、無理なときの最後の手段にする。
例:<input> に入力された文字を取り出す。
document.addEventListener("input", (e) => {
console.log(e.target.value); // エラー(親の EventTarget に value は無い)
console.log((e.target as HTMLInputElement).value); // OK(入力された文字)
});
-
e.targetの型はEventTarget(親の型)で、valueが無い -
HTMLInputElement(<input>の型)は、その子で、valueがある -
asで、親から子の型に絞っている
非 null アサーション
値!
null / undefined ではないと上書きする。
例:id="app" の要素に、文字を入れる。
document.getElementById("app").textContent = "Hello"; // エラー(null かもしれない)
document.getElementById("app")!.textContent = "Hello"; // OK
-
getElementById("app")の型はHTMLElement | null(要素、またはnull) -
nullかもしれない値には.textContentを使えないので、エラーになる - 要素があると分かっているので、
!で「nullではない」と教えて、エラーを消す
※ !true(=false) のように前に!がつくものは、論理否定
ジェネリクス
function 関数名<T>(引数: T): T
type 型名<T> = { プロパティ: T }
型を後から指定できるようにする。
定義側で <T> を宣言し、呼び出し側で T に入れる型を渡す。
function first<T>(arr: T[]): T {
return arr[0];
}
first<number>([1, 2, 3]); // number
first<string>(["a", "b"]); // string
引数から決まるなら、呼び出し側の <number> は省略できる。
first([1, 2, 3]); // number(推論される)
extends を付けると、T に入れられる型を絞れる。
例:id を持つ値だけ受け取る関数。
function getId<T extends { id: number }>(item: T) {
return item.id; // extends が無いと、T に id があるか分からずエラー
}
getId({ id: 1, name: "Taro" }); // OK(id がある)
getId({ name: "Taro" }); // エラー(id が無い)
-
T extends { id: number }:Tは「id: numberを持つ型」だけ - 絞ることで、関数の中で
item.idが使える -
id以外のプロパティ(nameなど)は、あってもよい
型にも付けられる。
type Box<T> = { value: T };
const a: Box<number> = { value: 1 };
const b: Box<string> = { value: "a" };
型引数にはデフォルト値も指定できる。
type Box2<T = string> = { value: T };
const c: Box2 = { value: "a" }; // T は string
typeof と keyof
typeof 値
keyof 型
typeof は、値から型を取り出す。
型を別に書かなくても、値の定義が元になる。
const user = { id: 1, name: "Taro" };
type User = typeof user;
// { id: number; name: string }
関数にも使える。ReturnType と組み合わせることが多い。
function create() {
return { id: 1 };
}
type Created = ReturnType<typeof create>;
// { id: number }
keyof は、型のキーをユニオン型にする。
type User = { id: number; name: string };
type Key = keyof User;
// "id" | "name"
typeof と組み合わせると、オブジェクトのキーの一覧が型になる。
const labels = { name: "名前", email: "メール" } as const; // as const は次の節
type LabelKey = keyof typeof labels;
// "name" | "email"
ジェネリクスと組み合わせると、存在するキーだけを受け取る関数を書ける。
function get<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}
get({ id: 1, name: "Taro" }, "name"); // string
get({ id: 1, name: "Taro" }, "age"); // エラー
T[K] は、T の K というキーの値の型を取り出す書き方(インデックスアクセス型)。
as const
値 as const
値の後ろに付けて、読み取り専用にする。
型も、string ではなくリテラルの値そのままに狭まる。
const config = { mode: "dark" } as const;
// { readonly mode: "dark" }
config.mode = "light"; // エラー
配列に付けると、タプル型(readonly [...])になる。
const roles = ["admin", "user"] as const;
// readonly ["admin", "user"]
typeof と [number] を組み合わせると、要素をユニオン型に展開できる。
type Role = (typeof roles)[number];
// "admin" | "user"
インデックス型
{ [key: キーの型]: 値の型 }
キーの名前は決まっていないが、値の型は揃っているオブジェクトの型。
例:科目名が決まっていない、点数の一覧。
type Scores = { [key: string]: number };
const scores: Scores = { math: 80, english: 70 };
scores.science = 90; // OK(キーは自由に足せる)
scores.art = "A"; // エラー(値は number だけ)
マップ型
{ [K in keyof T]: 型 }
T のキーを1つずつ K に入れて、新しい型を作る。
例:全プロパティを boolean にした型。
type User = { id: number; name: string };
type Flags<T> = { [K in keyof T]: boolean };
type UserFlags = Flags<User>;
// { id: boolean; name: boolean }
T[K] と書くと、元の値の型を引き継げる。
type Copy<T> = { [K in keyof T]: T[K] };
Record
Record<K, V>
インデックス型とマップ型を、同じ書き方で作れるようにした略記。
-
Kがstring:インデックス型 -
Kがユニオン型:マップ型
type A = Record<string, number>;
// { [x: string]: number } → インデックス型
type B = Record<"a" | "b", number>;
// { a: number; b: number } → マップ型
ユニオン型なら、キーの書き忘れや書き間違いをエラーにできる。
const label: Record<"name" | "email", string> = {
name: "名前",
email: "メール",
};
条件型
T extends A ? X : Y
条件によって決まる型。
T が A に当てはまれば X、そうでなければ Y になる。
extends の使い方は2つ。
- アノテーション:
T extends "admin" ? Admin : User- 条件で、型を
AdminかUserに決める
- 条件で、型を
- ジェネリクス:
<T extends "admin" | "user">-
Tに渡せる値を絞る
-
type Admin = { role: "admin"; permissions: string[] };
type User = { role: "user" };
type Account<T extends "admin" | "user"> =
T extends "admin" ? Admin : User;
type A = Account<"admin">; // Admin
type B = Account<"user">; // User
関数の戻り値に使うと、引数に応じて戻り値の型を変えられる。
declare function createAccount<T extends "admin" | "user">(
role: T
): T extends "admin" ? Admin : User;
createAccount("admin"); // Admin
createAccount("user"); // User
ユニオン型を渡すと、メンバーごとに条件が評価されて、結果がユニオンになる(分配条件型)。
type ToArray<T> = T extends unknown ? T[] : never;
type C = ToArray<number | string>;
// number[] | string[]
never で「消す」
"a" | never は "a"。
これを使うと、条件型で「この要素を消す」ことができる。
Exclude の中身がこれ。
type MyExclude<T, U> = T extends U ? never : T;
type R = MyExclude<"a" | "b", "a">;
// "a" → never(消える)
// "b" → "b"
// 結果: "b"
infer
T extends 型<infer U> ? U : never
条件型の中で、型の一部を取り出して変数にする。
extends の右側に infer 変数名 と書くと、その位置にある型が変数に入る。
例:配列の、要素の型を取り出す。
type ElementOf<T> = T extends (infer U)[] ? U : never;
type A = ElementOf<number[]>; // number
type B = ElementOf<string[]>; // string
-
(infer U)[]:「何かの配列」の形。Uは、その「何か」を入れる変数 -
number[]を渡すと、Uはnumber - 配列ではない型を渡すと、
never
例:関数の、戻り値の型を取り出す。
type MyReturnType<T> = T extends (...args: any[]) => infer R ? R : never;
type C = MyReturnType<() => number>; // number
-
(...args: any[]) => infer R:「何かの関数」の形。Rは、その戻り値の型 -
() => numberを渡すと、Rはnumber - 組み込みの
ReturnTypeは、これと同じ作り
ユーティリティ型
Partial<T>
Required<T>
Readonly<T>
Pick<T, K>
Omit<T, K>
よく使う型の変換が、組み込みで用意されている。
中身は、ここまでのマップ型や条件型でできている。
以下 User 型を使って説明
type User = { id: number; name: string; email: string };
| ユーティリティ型 | 意味 | 結果 |
|---|---|---|
Partial<User> |
全プロパティを省略可能に | { id?: number; name?: string; email?: string } |
Required<User> |
全プロパティを必須に | すべて必須 |
Readonly<User> |
全プロパティを読み取り専用に | すべて readonly
|
Pick<User, "id" | "name"> |
指定したキーだけ残す | { id: number; name: string } |
Omit<User, "email"> |
指定したキーを除く | { id: number; name: string } |
Exclude<"a" | "b", "a"> |
ユニオンから除く | "b" |
Extract<"a" | "b", "a"> |
ユニオンから抜き出す | "a" |
NonNullable<string | null> |
null と undefined を除く |
string |
ReturnType<typeof f> |
関数の戻り値の型 | 戻り値の型 |
Parameters<typeof f> |
関数の引数の型 | タプル型 |
Awaited<Promise<string>> |
Promise の中身 |
string |
組み合わせて使うことも多い。
// id 以外を、更新用に省略可能にする
type UpdateUser = Partial<Omit<User, "id">>;
satisfies
値 satisfies 型
型チェックだけし、実際の型は値から推論する
type Palette = Record<"red" | "green", string | number[]>;
アノテーションだと、red も green も string | number[] 扱いになる。
const a: Palette = {
red: [255, 0, 0],
green: "#00ff00",
};
a.red.map((n) => n * 2); // エラー(number[]ではなくstring かもしれない)
satisfies なら、red は number[]、green は string のまま使える。
const b = {
red: [255, 0, 0],
green: "#00ff00",
} satisfies Palette; // bがPaletteの型に合うかは検査(型定義はしない)
b.red.map((n) => n * 2); // OK(number[])
b.green.toUpperCase(); // OK(string)
もちろん、型に合わないとエラーになる(キーの書き間違いなど)。
const c = {
red: [255, 0, 0],
gren: "#00ff00", // エラー(Palette にないキー)
} satisfies Palette;
as const と組み合わせると、チェックしつつ最も狭い型になる。
const routes = {
home: "/",
about: "/about",
} as const satisfies Record<string, `/${string}`>;
// routes.home の型は "/"
使い分け:
| 書き方 | チェック | 型 |
|---|---|---|
const x: 型 = 値 |
する | 指定した型 |
const x = 値 satisfies 型 |
する | 値から決まる型 |