LoginSignup
0
0

More than 1 year has passed since last update.

TypeScript - オブジェクトの型からプロパティの型を抽出する方法

Posted at

はいさい!ちゅらデータぬオースティンやいびーん!んな、がんじゅーやみ?

概要

TypeScriptでオブジェクトの型(type)から、そのオブジェクトのプロパティの型を抽出する方法を紹介します。

また、オブジェクトの型からではなく、推測(Infer)された型からプロパティの型を抽出する方法も紹介します。

最後に、JSDocsで同様なことをどのようにできるか、書いておきます。

問題の例

Reactでメモのリストを表示するアプリを作っています。

メモの型は以下の通り。idがややこしい!

@types/index.ts
export type Memo = {
    id: string | number | symbol;
    body: string;
};

そこで、コメントを削除するロジックを追加します。

Comments.tsx
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));

しかし、このような書き方は冗長だし、Memoidの方が後々のリファクタリングで変わったら、修正する箇所が増えます。

解決法

以下のようにすれば、Memoidプロパティの型を抽出することができます。

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の例

Memos.jsx
//@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のこういった便利な機能を見つけていきたいと思います!

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0