npm run huequica:dev

なんか長めのはなしとか

TypeScript の `String#toString` を使うと返却型が `string` にできる裏技(伊東家の食卓)

なにを言ってるんだァおめー?と思っている人が居たらそれは正しくて、あたしもこのタイトルを書いておいて(コンテキスト0だとあたりまえ体操だな)となっているワケですわね。

例題

2つの等しい value を持つ object が存在するとして、共通の value のみ残した object を作りたいと思う

const values = {
    hoge: 'fuga',
    moge: 'piyo',
} as const;

const searchTarget = {
    hoge: 'fuga',
    moge: 'boeeeee',
};

// values と searchTarget で共通の value を持つキーのみ残したものを作りたい
// const newValue = { hoge: 'fuga' }; // のような

このとき Object#fromEntriesObject#entries を使って以下のような実装をすると思う

Object.fromEntries(
    Object.entries(searchTarget).filter(
        ([_, text]) => Object.values(values).includes(text)
    )
)

js のコンテキストとしては問題ない、がTS のコンテキストだとエラーになる

なにが問題?

エラー出力の内容の通りなのだが Object.values で引っ張ってきた配列の型が 'fuga' | 'piyo' になっていて Object.entries(searchTarget) で引っ張ってくる textstring のため適合しないと言われる

解法

1. toString() を経由した string へのダウンキャスト

社で教えてもらった方法。 String#toString を 利用( 悪用 ) しつつ Array#some で検索するようにする

Object.fromEntries(
    Object.entries(searchTarget).filter(
        ([_, text]) => Object.values(values)
        .some((value) => value.toString() === text) // value の定義は 'fuga' | 'piyo'
    )
)

Array の存在確認系の実装は脳死includes() 使いがちだったのだが some() を使えば中でごにょごにょできる。 その上で value に対して toString を使う。すると型定義が string にダウンキャストされる

これによって型が適合する

2. Object.values に対してジェネリクスstring を渡す

身内の Discord のメンバーの人に教えてもらった方法。こちらはタイトルと関係ないがスマートなのでご紹介。

Object.fromEntries(
    Object.entries(searchTarget).filter(
        ([_, text]) => Object.values<string>(values).includes(text)
    )
)

Object.values が実はジェネリクスを受け取れるのでこれに string を渡してやる。 すると Object.values の返却型が string[] となるので includes() を使用しても問題なくなる。

playground

Playground のリンクを載せておくので興味がある人は触ってみると良いかも。

www.typescriptlang.org