はいさい!ちゅらデータぬオースティンやいびーん!んな、がんじゅーやみ?
概要
TypeScriptでオブジェクトの型(type)から、そのオブジェクトのプロパティの型を抽出する方法を紹介します。
また、オブジェクトの型からではなく、推測(Infer)された型からプロパティの型を抽出する方法も紹介します。
最後に、JSDocsで同様なことをどのようにできるか、書いておきます。
問題の例
Reactでメモのリストを表示するアプリを作っています。
メモの型は以下の通り。id
がややこしい!
export type Memo = {
id: string | number | symbol;
body: string;
};
そこで、コメントを削除するロジックを追加します。
import { useState } from "react";
import type { Memo } from "@types/index";
const memos: React.FC = () => {
const [memos, setMemos] = useState<Memo[]>([]);
const deleteMemo = (id /** ここの型をどうする? */ ) =>
setMemos(prev => prev.filter(memo => memo.id !== id));
return (
<ul>
{memos.map(memo => (
<li key={String(memo.id)} onClick={deleteMemo.bind(null, memo.id)}>
{memo.body}
</li>
))}
</ul>
);
};
export default memos;
しかし、このdeleteMemo
の関数の引数の方をどうしましょうか?
もちろん、以下のような書き方ができます。
const deleteMemo = (id: string | number | symbol) =>
setMemos(prev => prev.filter(memo => memo.id !== id));
しかし、このような書き方は冗長だし、Memo
のid
の方が後々のリファクタリングで変わったら、修正する箇所が増えます。
解決法
以下のようにすれば、Memo
のid
プロパティの型を抽出することができます。
const deleteMemo = (id: Memo["id"]) =>
setMemos(prev => prev.filter(memo => memo.id !== id));
意外と簡単!
こうすると、大元のMemo
型が変わっても、TypeScriptがエラーが吐かないのでありがたいです。
型なしのオブジェクトから抽出する方法
以下のように推測された型からも方を抽出することができます。
const comment = { id: 1, body: "Hello", title: "First" };
type CommentId = (typeof comment)['id']; // number
また、以下のように、配列からも抽出できます。
const comments = [
{ id: 1, body: "Hello", title: "First" },
{ id: 2, body: "World", title: "Second" },
{ id: 3, body: "Wide", title: "Third" },
];
type CommentId = (typeof comments)[0]['id']; // number
JSDocsの構文
上記の型抽出はJSDocsでも可能です。
Reactの例
//@ts-check
/** @typedef {{id: string | number | symbol; body: string;}} Memo */
import React, { useState } from "react";
/** @type {React.FC} */
const memos = () => {
const [memos, setMemos] = useState(/** @type {Memo[]} */ ([]));
/** @param {Memo["id"]} id */
const deleteMemo = id =>
setMemos(prev => prev.filter(memo => memo.id !== id));
return (
<ul>
{memos.map(memo => (
<li key={String(memo.id)} onClick={deleteMemo.bind(null, memo.id)}>
{memo.body}
</li>
))}
</ul>
);
};
export default memos;
Commentsの例
const comments = [
{ id: 1, body: "Hello", title: "First" },
{ id: 2, body: "World", title: "Second" },
{ id: 3, body: "Wide", title: "Third" },
];
/** @typedef {(typeof comments)[0]['id']} CommentId */
まとめ
以上、TypeScriptでオブジェクトのプロパティの型を抽出する方法を紹介してきました。
TypeScriptの公式ドキュメントにこの方法を探してもなかなか見つからなかったので、もしかして
と思ってやってみたら、できたので共有したくなりました。
また、配列として定義したタイプも同様に[0]
と指定すると、開梱してその中の型を抽出することもついでに発見したので、ますます面白くなりました。
今後も、ぜひ、TypeScriptのこういった便利な機能を見つけていきたいと思います!