31
20

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

TypeScript3.1 で ReduxAction型定義は不要になりました

Last updated at Posted at 2018-10-02

MappedTypes で Unpacked が可能に

従来 Tuple や Promise に利用できた ConditionalTypes による Unpacked が MappedTypes でも可能になりました。まずは公式に掲載されている Unpacked型 を見てみます。戻り型を抽出している件は謎ですが、要するに Unwrapper と解釈して問題ないでしょう。

type Unpacked<T> =
    T extends (infer U)[] ? U :
    T extends (...args: any[]) => infer U ? U :
    T extends Promise<infer U> ? U :
    T;

これが v3.1 から次の様に MappedTypes でも可能になりました。個人的にかなり大きい変更点だと思っていますが、公式ではまだアナウンスされていません。ChildNode の UnionTypes を得ることができます。これが、本題の件に影響を与えています。

type Unwrap<T> = T extends {[K in keyof T]: infer U} ? U : never

本題

タイトルで言及している「不要」とは、型定義のハードコーディングを指します。ObjectMap から UnionTypes を得ることができるので、ActionCreators ファイルから直接 Actions型を推論することができます。こんな感じの ActionCreators ファイルがあるとします。いたって普通ですが、ActionType は StringLiteralTypes でないとマズイので、Assertion をつけておきます。戻り型の Annotation は付与しません。

creators.ts
export function increment() {
  return { type: 'INCREMENT' as 'INCREMENT' }
}
export function decrement() {
  return { type: 'DECREMENT' as 'DECREMENT' }
}
export function setCount(amount: number) {
  return { type: 'SET_COUNT' as 'SET_COUNT', payload: amount }
}
export function setName(value: string) {
  return { type: 'SET_NAME' as 'SET_NAME', payload: value }
}

少し加工を加えないと期待する UnionTypes にはならないので、組み込み UtilityType の ReturnType で戻り型の推論導出をしておきます。これに、上記の Unwrapper を併せると次の様になります。CreatorsToActions 型 と称して再利用できる様に書き出しておきます。

definition.ts
type Unwrap<T> = T extends {[K in keyof T]: infer U} ? U : never
type ReturnTypes<T> = {
  [K in keyof T]: T[K] extends (...args: any[]) => any ?
                  ReturnType<T[K]> :
                  never
}
export type CreatorsToActions<T> = Unwrap<ReturnTypes<T>>

定常開発での利用例

import した ActionCreators ファイルを、CreatorsToActions型で変換します。あとは reducer の引数にキャストすればOK。下段は Actions型の内訳です。

reducer.ts
import * as creators from './creators'
import { CreatorsToActions } from 'path/to/definition'
type Actions = CreatorsToActions<typeof creators>
function reducer(state: State, action: Actions) {
  // ...
}
type Actions = {
  type: 'INCREMENT';
} | {
  type: 'DECREMENT';
} | {
  type: 'SET_COUNT';
  payload: number;
} | {
  type: 'SET_NAME';
  payload: string;
}

大袈裟なヘルパーモジュールを利用せずに、この定義が自動で推論出来る時代。幸せですね。

宣伝

この変更で可能になった様々な型変換について書いた同人誌を、技術書典5で頒布します。技術書典にお越しの方はお立ち寄り頂けると嬉しいです。サークルは 【か73】潜推艦 です。

31
20
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
31
20

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?