对元组数组进行类型检查

问题描述 投票:0回答:1

我正在尝试编写和类型检查一个元组数组,将动作创建器与其减速器配对,并且需要验证:

  • 该操作的有效负载与减速器的有效负载相同
  • 所有减速器都接受相同的状态

这个想法是以与库无关的方式编写它。

type A = string;
type B = number;
type State = { a: A; b: B };
type Action<Payload> = { type: string; payload: Payload };
type ActionFn<Payload> = (payload: Payload) => Action<Payload>;

const actionA: ActionFn<A> = (payload: A) => ({ type: 'A', payload });
const actionB: ActionFn<B> = (payload: B) => ({ type: 'B', payload });

const reducerA = (state: State, a: A): State => ({ ...state, a });
const reducerB = (state: State, b: B): State => ({ ...state, b });


type ActionReducerPair<State, Payload> = [
  ActionFn<Payload>,
  (state: State, payload: Payload) => State
];

type Reducer<State, Payload> = (state: State, payload: Payload) => State;

const reducers = [
  // These are OK
  [actionA, reducerA],
  [actionB, reducerB],

  // These should report an error
  // @ts-expect-error
  [actionB, reducerA],
  // @ts-expect-error
  [actionA, reducerB],
];

游乐场

所以我很清楚,我需要为每个条目提供一个通用上下文。我试过了:

  • 按顺序为每个条目调用一个函数

    这是可行的,但是我更愿意仅使用数据结构来表达解决方案。

    type Pair<State, Payload> = [
      ActionFn<Payload>,
      (state: State, payload: Payload) => State
    ];
    const pair = <State, Payload>(
      actionCreator: ActionFn<Payload>,
      reducerFn: (state: State, payload: Payload) => State
    ): Pair<State, Payload> => [actionCreator, reducerFn];
    
    const reducerPairs2 = [
      // These are OK
      pair(actionA, reducerA), // ✅
      pair(actionB, reducerB), // ✅
    
      // These should report an error
      // @ts-expect-error
      pair(actionB, reducerA), // ✅
      // @ts-expect-error
      pair(actionA, reducerB), // ✅
    ];
    
  • 使用类型助手强制执行类型

    这不太管用,要么全部通过,要么全部失败。

    type ValidPair<MaybeAction, MaybeReducer> = 
      MaybeAction extends ActionFn<infer Payload>
        ? MaybeReducer extends Reducer<infer State, Payload>
          ? ActionReducerPair<State, Payload>
          : 'reducer does not satisfy Reducer<S, P>'
        : 'action does not satisfy ActionFn<P>';
    
    const reducers = [
      // These are OK
      [actionA, reducerA], // ✅
      [actionB, reducerB], // ✅
    
      // These should give an error
      // @ts-expect-error // 💥 no error reported
      [actionB, reducerA],
      // @ts-expect-error // 💥 no error reported
      [actionA, reducerB],
    ] satisfies Array<ValidPair<ActionFn<any>, Reducer<State, any>>>;
    
typescript redux
1个回答
0
投票

我会在数组上使用条件类型来解决这个问题:

type Reducer<State, Payload> = (state: State, payload: Payload) => State;

const reducerA: Reducer<State, A> = (state: State, a: A): State => ({ ...state, a });
const reducerB: Reducer<State, B> = (state: State, b: B): State => ({ ...state, b });

type ActionReducerPair<State, Payload> = [
  ActionFn<Payload>,
  Reducer<State, Payload>
];

type ReducerArray<State, T> = T extends A ? ActionReducerPair<State, A>[]
  : T extends B ? ActionReducerPair<State, B>[]
  : never;

const reducers: ReducerArray<State, A | B> = [
  // These are OK
  [actionA, reducerA],
  [actionB, reducerB],

  // These now give an error
  [actionB, reducerA],
  [actionA, reducerB],
];

本质上,

ReducerArray
类型将允许打字稿将
Payload
类型检查为相应的
ActionReducerPair
类型。

显然,只有当你能够维持

ReducerArray
类型时,这个解决方案才有效。

© www.soinside.com 2019 - 2024. All rights reserved.