TypeScriptのコードを読んでいると、(x: number): number => や Pick<User, "id">、?. や ! のような記号が続けて出てきて、どこで区切って読めばいいのか迷うことがあります。C言語の関数ポインタの宣言を初めて読んだときに似た、「記号の意味が分かれば読めるのに」というもどかしさです。
この記事では、TypeScriptのコードによく出てくる記号を、「アロー関数の型」「ユーティリティ型」「null を扱う記号」に分けて読み解きます。アロー関数そのものの書き方は「アロー関数(=>)とは?」で紹介しているので、ここでは型の部分を中心に見ていきます。コードはTypeScript Playgroundに貼れば試せます。
記号の早見表
先に、この記事で扱う記号をまとめておきます。
| 記号 | 意味 | 例 |
|---|---|---|
名前: 型 | その変数や引数の型 | (x: number) |
?: | 省略できるプロパティ | age?: number |
型の中の => | 関数の戻り値の型 | (x: number) => number |
<T> | 型を後から決める(ジェネリクス) | <T>(items: T[]) => ... |
?. | null か undefined なら、そこで止めて undefined | user.address?.city |
?? | null か undefined なら、右の値を使う | city ?? "未登録" |
後ろに付く ! | null ではないと TypeScript に伝える | input!.value |
as | 型を上書きする | el as HTMLInputElement |
アロー関数の型の読み方
引数の型と戻り値の型
const double = (x: number): number => x * 2;この1行は、次の3つに分けて読めます。
| 部分 | 意味 |
|---|---|
(x: number) | 引数 x は number 型 |
) の後ろの : number | 戻り値は number 型 |
=> x * 2 | 中身(x * 2 を返す) |
) と => の間にある : number が戻り値の型です。C言語の int double_it(int x) は戻り値の型を先に書きますが、TypeScriptでは引数の後ろに書きます。
型の中に出てくる => は「戻り値の型」
=> は、関数の「型」を書くときにも使われます。こちらは中身ではなく、戻り値の型を表します。
// 「number を受け取って number を返す関数」という型
type NumberFn = (x: number) => number;
// 型が決まっているので、x の型は書かなくてよい
const triple: NumberFn = x => x * 3;type や変数の : の後ろなど、型を書く場所にある => は「戻り値の型」、= の右側の値の場所にある => は「アロー関数の中身」、と分けて読みます。C言語の typedef int (*NumberFn)(int); に当たるものです。
型を省けるのは、使われる場所から型が分かるときだけ
上の triple のように、引数の型を書かなくてよいのは、まわりから型が分かるときです。配列の map に渡す関数も、配列の中身から型が分かります。
const prices = [100, 250, 80];
const withTax = prices.map(p => p * 1.1); // p は number と分かる一方、何も手がかりがないところで引数の型を省くと、エラーになります。
const calc = x => x * 2; // x が何の型か分からないParameter 'x' implicitly has an 'any' type.「x の型が分からないので、何でも入る any 型になっている」という意味です。strict の設定(新しいTypeScriptでは初期設定でオン)では、これがエラーになります。短いコードを読むときは、「この引数の型はどこから来ているのか」を探すと、意味が分かりやすくなります。
<T> は「あとから決まる型」
関数の前に <T> が付いているのは、ジェネリクスです。T は「呼び出すときに決まる型」の付け替え用の名前で、数値の配列を渡せば number、文字列の配列を渡せば string になります。
// 配列の最初の要素を返す(配列が空なら undefined)
const firstOf = <T>(items: T[]): T | undefined => items[0];
const n = firstOf([10, 20]); // n は number | undefined
const s = firstOf(["a", "b"]); // s は string | undefinedT | undefined の | は「または」という意味です。Reactの .tsx ファイルでは、<T> がタグと紛らわしいので、<T,> と書かれていることもあります。
ユーティリティ型:元の型を加工する
Partial<User> のように <> で型を渡すものは、TypeScriptに最初からある「型を加工する型」(ユーティリティ型)です。「元の型」と「何をしたか」に分けて読むと分かりやすくなります。
type User = {
id: number;
userName: string;
email: string;
age?: number; // 省略できる
};
type UserDraft = Partial<User>; // すべて省略できるようにする(入力の途中など)
type FullUser = Required<User>; // すべて必須にする(age も必須)
type UserPreview = Pick<User, "id" | "userName">; // id と userName だけ取り出す
type PublicUser = Omit<User, "email">; // email だけ取り除く
type FixedUser = Readonly<User>; // すべて書き換えできなくする
type ScoreMap = Record<string, number>; // キーが文字列、値が数値のオブジェクトPick や Omit の2つ目には、プロパティ名を文字列で渡します。複数のときは "id" | "userName" のように | でつなぎます。エディタで UserPreview にマウスを当てると、加工したあとの型が表示されるので、読むときはそれを見るのがいちばん確実です。
null を扱う記号:?.、??、!
?. と ??:ないかもしれない値を安全に読む
type Profile = {
address?: { city?: string };
};
const profile: Profile = {};
// address がなければ、?. のところで止まって undefined になり、?? で "未登録" になる
const city = profile.address?.city ?? "未登録";
console.log(city); // 未登録?? と || の違いは「??(Null合体演算子)と三項演算子の違い」で紹介しています。
後ろの !:null ではないと「約束」する
変数の後ろに付く ! は、「この値は null や undefined ではない」と TypeScript に伝える記号です。ただし、実行するときには何も確かめません。本当に null だった場合は、そのままエラーになります。
// ! を使う書き方:input 要素がないと、実行時にエラーになる
const input1 = document.querySelector("input")!;
console.log(input1.value);
// 確かめてから使う書き方
const input2 = document.querySelector("input");
if (input2) {
console.log(input2.value);
}C言語でいえば、NULLチェックをしないままポインタを使うのに近い書き方です。読むときに ! を見つけたら、「ここは書いた人が null ではないと判断した場所」だと考えると、不具合を探すときの手がかりになります。document.querySelector については「querySelectorで使えるCSSセレクターの書き方」で紹介しています。
as と satisfies:型についての指示
// as:型を上書きする(実行時に値を変換するわけではない)
const priceInput = document.querySelector("#price") as HTMLInputElement;
// satisfies:型に合っているかを確かめる(推論された型はそのまま)
type Config = { port: number; host: string };
const config = { port: 3000, host: "localhost" } satisfies Config;as も ! と同じように、TypeScriptに「この型だと思ってくれ」と伝えるだけです。#price の要素がなければ priceInput は null になりますが、TypeScriptはそれに気づきません。satisfies は、型に合っていなければエラーにしつつ、値から推論した型をそのまま使える書き方です。
短いコードを読むときのコツ
=>を見たら、型の場所(戻り値の型)か、値の場所(関数の中身)かを分ける- 引数に型が書いていなければ、その型がどこから来ているか(map の配列、変数の型など)を探す
Partial<〜>などは、元の型と加工の内容に分けて読み、エディタで加工後の型を確かめる!とasは「書いた人の約束」。実行時には確かめられていない
分からない記号が出てきたら、Playgroundに貼ってマウスを当て、型を表示させてみるのが近道です。Playgroundの使い方は「公式サイト typescriptlang.org の使い方」、[a, b] のような配列の型は「タプル(Tuple)とは?」で紹介しています。
まとめ
- アロー関数では、
(x: number)が引数の型、)の後ろの: numberが戻り値の型 - 型の中の
=>は戻り値の型。(x: number) => numberは「number を受け取って number を返す関数」 - 引数の型を省けるのは、使われる場所から型が分かるときだけ
- ユーティリティ型は、元の型を加工する型(Partial、Required、Pick、Omit、Readonly、Record)



コメント