【redux】as const로 action creator로부터 Action의 형태를 간단하게 만든다

13218 단어 TypeScriptredux
3.4 이전에는, 형부를 하기 위해서 as assertion 에서 LiteralType 를 사용할 필요가 있어 번거로웠습니다. 하지만, TS3.4로부터 등장한 as const 에 의해 redux의 형 첨부는 매우 간편하게 되었습니다.



먼저 ActionCreator를 만듭니다. 이 때 type 속성에 as const를 붙이는 것보다 객체에 as const를 붙이는 것이 모든 속성이 readonly가 되므로 추천합니다.
const increment = (payload: number) =>
  ({
    type: 'increment',
    payload,
  } as const)

Action 형을 만드는 것은 이하와 같은 형태 정의가 필요합니다. 하고 있는 것은 간단하고 각각의 ActionCreator 로부터 ReturnType 그리고 반환값의 형태를 꺼내 그 Union형을 만듭니다. AnyAction 는 필수는 아니지만 type 프로퍼티를 반드시 포함하고 있는 것을 보증할 수가 있습니다.
import { AnyAction } from 'redux'

export type ActionsType<ActionCreators extends object> = {
  [Key in keyof ActionCreators]: ActionCreators[Key] extends (
    ...args: any[]
  ) => AnyAction
    ? ReturnType<ActionCreators[Key]>
    : never
}

export type ActionType<
  ActionCreators extends object,
  Actions = ActionsType<ActionCreators>
> = { [Key in keyof Actions]: Actions[Key] }[keyof Actions]

사용법으로서는, as const 첨부로 ActionCreator를 작성해, ActionType<typeof actions> 로 Action형을 작성할 수 있습니다.

actions.ts
export const increment = (payload: number) =>
  ({
    type: 'increment',
    payload,
  } as const)

export const decrement = (payload: number) =>
  ({
    type: 'decrement',
    payload,
  } as const)

export const typo = (payload: string) =>
  ({
    // typo...
    typo: 'typo',
    payload,
  } as const)

types.ts
import { ActionType } from './redux-actions-type'
import * as actions from './actions'

type Action = ActionType<typeof actions>
/**
 * type Action = {
 *   type: "increment";
 *   payload: number;
 * } | {
 *   type: "decrement";
 *   payload: number;
 * }
 */
type 속성뿐만 아니라 payload가 올바르게 추론되었음을 알 수 있습니다.
또, type 프로퍼티가 없는 것은 never 형태가 되기 때문에, Action형에는 나타나는 일이 없기 때문에 안심하고 사용할 수 있습니다.

그리고는, 보통으로 reducer 등으로 사용하는 것뿐입니다.

reducer.ts
import { Action } from './types'

type State = {
  count: number
}

export const reducer = (state: State = { count: 0 }, action: Action): State => {
  switch (action.type) {
    case 'increment':
      return { count: state.count + action.payload }
    case 'decrement':
      // 主題とはずれるが戻り値のStateを省略するとこの余計な`hoge`プロパティを検知できない。
      // reduxのReducer型を使っても同じように検知されないので注意
      // return { count: state.count - action.payload, hoge: 1 }
      return { count: state.count - action.payload }
    // Type Error...
    // case 'typo':
    //   return { count: action.payload }
    default:
      return state
  }
}

또, Action 는 reducer 마다 별개의 형태로 하는 것이 아니라, 프로젝트로 하나의 형태로 하는 쪽이 처리가 편합니다.

매번이 정의를 작성하는 것은 번거롭기 때문에 라이브러리에서 설치할 수 있습니다. (스타는 좋은 선택입니다 🤭)
akameco/redux-actions-type: easy typing of redux action
$ npm install redux-actions-type
or
$ yarn add redux-actions-type

types.ts
import { ActionType } from 'redux-actions-type'
import * as actions from './actions'

export type Action = ActionType<typeof actions>

또 부속으로 Actions 라고 하는 형태를 export 할 수 있습니다.
이것은 개별의 Action형을 취할 수 있으므로 사용할 필요가 있으면(별로 그러한 씬은 없습니다만) 사용할 수 있습니다. 대부분은 ActionCreater 자신의 ReturnType으로 끝납니다.
import { ActionsType } from 'redux-actions-type'
import * as actions from './actions'

export type Actions = ActionsType<typeof actions>
/**
 * type IncrementAction = {
 *   type: "increment";
 *   payload: number;
 * }
 */
type IncrementAction = Actions['increment']

TypeScript의 타입 매우 편리하고 좋네요. 앞으로의 진화에도 기대입니다.

TypeScript 3.4 피 c. 라고 r. 이 m/wY45XfZPV2 — 아카메@외로움에 걸어라. 악을 하지 않고, 요구하는 곳은 적고. 숲에서 코끼리처럼 .js (@akameco) April 21, 2019

akameco/redux-actions-type: easy typing of redux action htps // t. 코 / HPCKr 84 형이 없는 환경으로부터 간편하게 redux의 Action에 형 붙이는 곳까지 가져가는 사람입니다 피 c. 라고 r. 코 m / H p 7 8 jt 악을 하지 않고, 요구하는 곳은 적고. 숲에서 코끼리처럼 .js (@akameco) March 14, 2019

좋은 웹페이지 즐겨찾기