😽

TypeScript 4.9 でリリースされた satisfies をサンプルオブジェクトに活用できたはなし

2022/12/19に公開

Leaner 開発チームの phigasui です。

先日 TypeScript 4.9 がリリースされましたね。
開発チームのみんなで Announcing TypeScript 4.9 - TypeScript を見ていたのですが
satisfies が気になりつつも実際コードでどこで活用できるかイメージできていませんでした。

今回、テストコード向けに用意しているサンプルのオブジェクトでの活用例を紹介します。

今回紹介するケース

弊プロダクトではフロントエンドのテスト用のダミーデータのオブジェクトを定義して使っています。
フロントエンドは SPA になっており、API リクエストのレスポンスをモックしてそのオブジェクトを返すようにしています。
例えばユーザーのデータなら下記のように定義しています。

examples.ts
type User = {
  id: number
  email: string
  name?: string
}

export const user: User = {
  id: 1,
  email: 'user1@example.com',
  name: 'user1',
}

例として、ユーザー編集ページでのフロントエンドのスモークテストでは下記のように確認しています。
ユーザー編集のために既存の情報を取得してフォームに入力されている状態を検証しています。

test.ts
  const user = examples.user // 上記で定義したサンプルのオブジェクト

  expect(await screen.findByText(user.name)).toBeVisible()

発生する問題


tsc でエラーになっている様子

tscの結果
No overload matches this call.
  Overload 1 of 2, '(id: Matcher, options?: SelectorMatcherOptions | undefined, waitForElementOptions?: waitForOptions | undefined): Promise<HTMLElement>', gave the following error.
    Argument of type 'string | undefined' is not assignable to parameter of type 'Matcher'.
      Type 'undefined' is not assignable to type 'Matcher'.
  Overload 2 of 2, '(id: Matcher, options?: SelectorMatcherOptions | undefined, waitForElementOptions?: waitForOptions | undefined): Promise<HTMLElement>', gave the following error.
    Argument of type 'string | undefined' is not assignable to parameter of type 'Matcher'. [2769]

この時、findByText の引数は undefined を期待しておらず、User['name']string | undefined のため、型が合いません。
そのため、Non-null assertion operator を使うなど、undefined を回避する必要がありました。

  expect(await screen.findByText(user.name!)).toBeVisible()

現実として(型として)はたしかに、undefined になりうるものの、テストで使う値は決まっているのに undefined になりうることを考慮したコードにしないといけないのはすこしネックでした。

satisfies というソリューション

そこで user に対して User で型づけするのでなく satisfies Operator を活用します。

export const user = {
  id: 1,
  email: 'user1@example.com',
  name: 'user1',
} satisfies User

そうすると user は定義されたオブジェクトのインターフェイスのまま推論できるため
namestring | undefined でなく string で推論されます。
なのでテストコードで undefined を回避する必要がなくなります。


tsc でおこられなくなった様子

  expect(await screen.findByText(user.name)).toBeVisible()

かつ user のオブジェクトは User の type に合っているかがチェックされるので User type が変更されてサンプルのオブジェクトが更新できていない、なんて時にも検知できるので型安全にできます。

感想

TypeScript 4.9 でリリースされた satisfies とても便利で役にたちました。
リリースしてすぐチームでキャッチアップし活用できている状態も良いチームだなという気持ちです。
そして、今回の活用例をコードレビューでチームメンバーが気付いてくれたので非常に感謝しております🙏

宣伝

Leaner では最新技術でコードを改善していきたいエンジニアを募集しています🙌

https://careers.leaner.co.jp/engineering

リーナーテックブログ

Discussion