かもメモ

自分の落ちた落とし穴に何度も落ちる人のメモ帳

TypeScript interface のプロパティの型を取得したい!

オブジェクトがリストになっているデータから、オブジェクト内の id をキーにした Map を作成したい時、id の型を interface から取得したかったのメモ

interface

interface Iidol {
  id: number;
  name: string;
  tyep: 'cute' | 'cool' | 'sexy' | 'pop';
}

1. interface のプロパティを直接指定して型を作ることができる

type IdolIdType = Iidol['id'];
// => type IdolIdType = number

2. プロパティ名のリテラル型を作成して、それを interface のプロパティとして型を作成

type IdolId = 'id';
type IdolIdType = Iidol[IdolId];
// => type IdolIdType = number

上記の方法で interface から type を作成することができました。
ただ個人的には interface からプロパティの型を取得する必要が出てしまうのであれば、最初からそのプロパティの型を定義しておくほうが見通しが良さそうと感じました。

type IdolIdType = number;

interface Iidol {
  id: IdolIdType;
  name: string;
  tyep: 'cute' | 'cool' | 'sexy' | 'pop';
}

IdolId をキーにした Map を作成する

type Idols = Iidol[];

このデータを Map にする

const idols: Iidol[] = [
  { id: 1, name: '星宮いちご', type: 'cute'},
  { id: 2, name: '霧矢あおい', type: 'cool'},
  // ...
];

const getIdoMap = (): ReadonlyMap<IdolIdType, Iidol> => {
  const map = new Map();
  idols.forEach((user) => {
    map.set(user['id'], user);
  });

  return map;
}

getIdoMap();

いい感じになりましたが getIdoMapidols が外にあって副作用があるし key が固定なので、もう少し抽象化してみます。

const getMapByKey =
  <T, K extends keyof T>(data: T[]) =>
  (key: K): ReadonlyMap<T[K], T> => {
    const map = new Map();
    idols.forEach((item) => {
      map.set(item[key], item);
    });

    return map;
  };

getMapByKey(idols) => ('id');

API から渡される オブジェクトの入った配列からキーを指定して Map が作れるようになりました!
interafce から型作らなくてもよかった…

所管

interface をオブジェクトのように扱うことでプロパティの型を作成することができる (Interface['property name'] )
ただ特定のプロパティの型が外部で必要であれば別途定義しておくほうが見通しが良い。
特定のプロパティの型が無くても成立する方法がないか考えてみるのが良い。

こんな感じでしょうか。
TypeScript のジェネリクス何となく理解してきました。T とか K って何なん?って思っていたのですが、抽象的な型を表すのに伝統的に使われているだけだったと知ってから苦手意識がなくなりました。 ループの i とか j みたいなものだったのか〜


[参考]

BADON 新刊出てた!!