flowの$PropertyTypeと$ElementTypeの違い

型定義したファイルをimportして使うときの話

$PropertyType

JavaScriptでflowを使って型を定義する場合、 型の定義自体は別ファイルで行いそれをimportして使うことができます

例えばReactで本の情報を登録する画面を作ろうと思い、 本のid、タイトル、著者名をstateで管理します

以下のようなディレクトリ構成の場合

javascript
    component
        books.js
    type
        book.js

type/book.jsで肩を定義し

// type/book.js

// @flow

type Book = {|
    id:     number,
    title:  string,
    author: string,
|};

export default Book;

component/books.jsで読み込んで使います

// component/books.js

// @flow

import { Book } from '../type/book';

type State = {|
    id:     $PropertyType<Book, 'id'>,
    title:  $PropertyType<Book, 'title'>,
    author: $PropertyType<Book, 'author'>
|}

上のように$PropertyTypeを使うとすでに定義してあるBook typeの idやtitleを使用しているということをわかりやすく書くことができます

$ElementType

ただ毎度毎度$PropertyTypeと書くのも数が少ないうちはいいですが増えていくとかなり見辛くなります 先輩に $EntityType使って書いた方が見やすいよと言われました さっきのコードを書き直すと下のようになります

// component/books.js

// @flow

import { Book } from '../type/book';

type BET<k> = $ElementType<Book, k>;

type State = {|
    id:     BET<'id'>,
    title:  BET<'title'>,
    author: BET<author'>
|}

さっきまでわざわざ $PropertyTypeって書いてたところを自分で命名できるのでかなり見やすくなりました

でもこの二つって何が違うんでしょう?

$PropertyTypeと$EntityTypeの違い

時間があるひとは公式を見てください

flow.org

flow.org

flowの公式でも$ElementTypeの説明にこう書いてあります

we’re using literal values as K, similarly to $PropertyType<T, k>. However, when using $ElementType<T, K>, K is allowed to be any type, as long as that type exists on the keys of T.

$ElementTypeを使用する場合は、第一引数に定義した型のkeyとして存在するものならなんでも 第二引数に渡すことができます

じゃあ$PropertyTypeはどうなんでしょう? $PropertyTypeを使って

type BET<k> = $PropertyType<Book, k>;

と書くと以下のようなerrorが出ます

Cannot use `$PropertyType` because the second type argument must be a string literal.

$PropertyTypeは第二引数に文字列しか許してくれないので、上の例のように型がわからないkを渡した時点でエラーが出ます $ElementTypeは実際に使用されて、kに入ってきた値がkeyとして存在しないとわかったタイミングでエラーになります

ElementTypeの方が使い勝手良さそうだなあ、、、