TypeScript のさわり

Part 0 · React の前に

TypeScript のさわり

型があると、なぜ書くのが楽になるのか

TypeScript は、JavaScript に「この値はこういう形をしている」という説明を書き足せるようにしたものです。別の言語ではありません。

型は書く手間が増えるだけに見えますが、実際は逆です。書く量より、間違いを探す時間のほうがずっと減ります。

この章では、React を読むために最低限必要なところだけをやります。 全部を覚える必要はありません。

型があると何が変わるか

型がないと、間違いは動かしてみるまで分かりません

// JavaScript
const user = { name: "さとう" };

console.log(user.nmae); // undefined(打ち間違いに気づけない)

nmae と打ち間違えても、JavaScript は何も言わずに undefined を返します。 画面に何も出ない理由を探して何十分も溶かす、というのがよくある流れです。

型を書いておくと、書いているその場で赤線が引かれます。

// TypeScript
type User = { name: string };

const user: User = { name: "さとう" };

user.nmae; // エラー: User に nmae はありません

member-type.ts

// データの形に名前をつける
type Member = {
  id: number;
  name: string;
  // ? を付けると「なくてもいい」項目になる
  nickname?: string;
};

const members: Member[] = [
  { id: 1, name: "さとう", nickname: "さっちゃん" },
  { id: 2, name: "すずき" },
  { id: 3, name: "たかはし", nickname: "たか" },
];

// nickname は undefined のことがあるので ?? で補う
export const labels = members.map((member) => member.nickname ?? member.name);

書き方は「値のうしろに : 型」

const name: string = "さとう";
const age: number = 20;
const isAdmin: boolean = false;

// 配列は「何の配列か」を書く
const names: string[] = ["さとう", "すずき"];

// オブジェクトは形をそのまま書く
const user: { name: string; age: number } = { name: "さとう", age: 20 };

ただし、ほとんどの場合型を書く必要はありません。 代入した値から自動で判断してくれるからです。

const name = "さとう"; // string だと分かってくれる

型を明示するのは、主に関数の引数データの形を決めるときです。 React では、これが props の型になります。

type で形に名前をつける

同じ形を何度も書くのは大変なので、名前をつけて使い回します。

type Member = {
  id: number;
  name: string;
  nickname?: string; // ? は「なくてもいい」
};

? を付けた項目は、あってもなくても構いません。 その代わり、使うときは「ないかもしれない」前提で扱う必要があります。

あってもなくてもいい項目

nickname がない人は name で表示される

あだ名があればそちら、なければ名前

  • さっちゃん
  • すずき
  • たか

member.nickname ?? member.name?? は前の章でやったものです。 TypeScript は「nickname は undefined かもしれない」と知っているので、 この対処を書かずに使おうとすると警告してくれます。型が、書き忘れを防いでくれているわけです。

member-type.ts

// データの形に名前をつける
type Member = {
  id: number;
  name: string;
  // ? を付けると「なくてもいい」項目になる
  nickname?: string;
};

const members: Member[] = [
  { id: 1, name: "さとう", nickname: "さっちゃん" },
  { id: 2, name: "すずき" },
  { id: 3, name: "たかはし", nickname: "たか" },
];

// nickname は undefined のことがあるので ?? で補う
export const labels = members.map((member) => member.nickname ?? member.name);

取りうる値を決めてしまう

TypeScript でいちばん便利なのが、「この値はこの 3 つのどれか」と決められることです。

type Status = "todo" | "doing" | "done";

const status: Status = "doing"; // OK
const wrong: Status = "yet";    // エラー

縦棒(|)で区切って並べるだけです。これを union と呼びます。 文字列を直接書けるので、ステータスや種類を表すのにぴったりです。

状態ごとの表示を作る

status に想定外の値が入る余地がない

  • 資料をまとめる完了
  • レビューを依頼する作業中
  • 公開する未着手

Record<Status, string> は 「Status のすべてに対して文字列を用意する」という意味です。 あとから Status に種類を足すと、用意し忘れた場所がエラーで教えてもらえます。 これが型のありがたみを最も感じる瞬間です。

union-type.ts

// 取りうる値そのものを列挙して、型にできる
export type Status = "todo" | "doing" | "done";

// Status のすべてに対して、ひとつずつ用意することを強制される。
// 増やし忘れがあるとエラーになる
const statusLabel: Record<Status, string> = {
  todo: "未着手",
  doing: "作業中",
  done: "完了",
};

const tasks: { title: string; status: Status }[] = [
  { title: "資料をまとめる", status: "done" },
  { title: "レビューを依頼する", status: "doing" },
  { title: "公開する", status: "todo" },
];

export const rows = tasks.map((task) => ({
  title: task.title,
  label: statusLabel[task.status],
}));

React ではこう使う

型を書く場所は、React ではほぼ 1 か所です。コンポーネントが受け取る値(props)のところ。

type Props = {
  name: string;
  age?: number;
};

function Profile({ name, age }: Props) {
  return <p>{name}({age ?? "非公開"})</p>;
}

ここには、Part 0 でやったことがすべて出てきています。

  • { name, age } … 分割代入
  • : Props … 型注釈
  • age? … あってもなくてもいい項目
  • ?? … ないときの代わりの値

この形が読めるようになれば、React のコンポーネントは ほぼすべて読めます。Part 2 でここを本格的に扱います。

member-type.ts

// データの形に名前をつける
type Member = {
  id: number;
  name: string;
  // ? を付けると「なくてもいい」項目になる
  nickname?: string;
};

const members: Member[] = [
  { id: 1, name: "さとう", nickname: "さっちゃん" },
  { id: 2, name: "すずき" },
  { id: 3, name: "たかはし", nickname: "たか" },
];

// nickname は undefined のことがあるので ?? で補う
export const labels = members.map((member) => member.nickname ?? member.name);

理解できたか確かめる

確認クイズ

type Member = { name: string; nickname?: string } の ? は何を意味する?

確認クイズ

type Status = 'todo' | 'doing' | 'done' の利点は?

member-type.ts

// データの形に名前をつける
type Member = {
  id: number;
  name: string;
  // ? を付けると「なくてもいい」項目になる
  nickname?: string;
};

const members: Member[] = [
  { id: 1, name: "さとう", nickname: "さっちゃん" },
  { id: 2, name: "すずき" },
  { id: 3, name: "たかはし", nickname: "たか" },
];

// nickname は undefined のことがあるので ?? で補う
export const labels = members.map((member) => member.nickname ?? member.name);

この章のまとめ

  • TypeScript は JavaScript に「値の形の説明」を足したもの
  • 型の利点は、間違いを実行する前に見つけられること
  • ほとんどは自動で判断されるので、書くのは関数の引数とデータの形くらい
  • ? は任意の項目、| は「このどれか」
  • React では props の型として使う。Part 2 で本格的に扱う

member-type.ts

// データの形に名前をつける
type Member = {
  id: number;
  name: string;
  // ? を付けると「なくてもいい」項目になる
  nickname?: string;
};

const members: Member[] = [
  { id: 1, name: "さとう", nickname: "さっちゃん" },
  { id: 2, name: "すずき" },
  { id: 3, name: "たかはし", nickname: "たか" },
];

// nickname は undefined のことがあるので ?? で補う
export const labels = members.map((member) => member.nickname ?? member.name);