All Articles

TypeScript Generics + Omitが便利

どの言語でも体感しているのですが、Generics がしっかり書けると、コードが簡潔にそして何よりカッコよく書ける!

まず、特定の Object からidを取り除いた型を用意したい場合、Generics を使わないと 2 つのインターフェイスを用意しないといけない。※1

idがないだけなのに冗長的でかなり残念です。このようなコードを書くとレビュー指摘は必至でしょう。

interface User {
    id: string
    sei: string
    mei: string
    age: number
    address: string
}

interface UserOmitId {
    sei: string
    mei: string
    age: number
    address: string
}

※1 Type を使っても完結に書けます。

type UserOmitId = {
    sei: string
    mei: string
    age: number
    address: string
}

type HasId = { id: string }

type User = UserOmitId & HasId

Omit で書くと

TypeScript のライブラリの utility-types の 1 機能の Omit を使うと以下のように書ける。

type OmitId<T> = Omit<T, "id">

型 T の中から、指定したキー名のプロパティを除外した新しい型を返す。

Omit<User, "id">

これと Generics を組み合わせると

id の要素を削ったオブジェクトを返却する関数を定義することができる。

User 型に依存しないで、かなり汎用的に使えますね。

function removeId<T extends HasId>(obj: T): OmitId<T> {
    const result = { ...obj }
    delete result.id // IDキーを除外する
    return result
}

試しに、Jest でテストコードを書くとこのようになります。

    test("OmitId test", () => {
        const user: User = {
            id: "1",
            sei: "Tanaka",
            mei: "Taro",
            age: 30,
            address: "東京都新宿区"
        }

        const expectedUser = {
            sei: "Tanaka",
            mei: "Taro",
            age: 30,
            address: "東京都新宿区"
        }

        const result = removeId(user)
        expect(result).toEqual(expectedUser)
    })

ちょっと応用

上の User は、 sei mei を結合して name にした型を返却する場合は、

type UserName = { name: string }

type UserSeiMei = { sei: string; mei: string }

※今回は Type で型を定義しましたが、Interface でも可能です。

function concatName<T extends UserSeiMei>(obj: T): T & UserName {
    const name = `${obj.sei} ${obj.mei}`
    const result = { ...obj }
    delete result.sei
    delete result.mei
    return { ...result, name }
}

このように sei meiのキーから値を取り出して結合して、nameを作成して、sei meiのキーを削除

    test("concat user name", () => {
        const user: User = {
            id: "1",
            sei: "Tanaka",
            mei: "Taro",
            age: 30,
            address: "東京都新宿区"
        }

        const expectedUser = {
            id: "1",
            name: "Tanaka Taro",
            age: 30,
            address: "東京都新宿区"
        }

        const result = concatName(user)
        expect(result).toEqual(expectedUser)
    })

まとめ

今回は id を除外 & 姓名を結合するような、簡単な例ではありましたが、活用すると冗長になりがちなコードを簡潔に書けますね。 カッコ良いコードを書けるように学習を進めたいと思います。