打字稿 |通用可索引类型中缺少 属性

Typescript | missing property in generic indexable type

我很好奇 Redux Toolkit 是如何工作的,并试图复制一个基本的实现。但是我 运行 遇到了一个关于可索引类型 AnyAction 和具有定义的 属性 类型 PayloadAction 的问题 payload.

builder.addCase 函数需要一个 reducer 函数作为第二个参数。该减速器又具有 2 个参数 stateactionaction 参数默认为 AnyAction。当我手动将 action 的类型设置为 PayloadAction<P> 时,会弹出一个关于 Action<string>.

类型中缺少 属性 payload 的错误

我似乎无法理解它,希望有人能帮助我并向我解释为什么这不起作用。

link to TS playground

export interface Action<T extends any = string> {
    type: T
}

export interface AnyAction extends Action {
    [extraProps: string]: any
}

export interface PayloadAction<P = any> extends Action {
    payload: P
}

const builder = {
    addCase(
        typeOrActionCreator: string, reducer: <A extends Action = AnyAction>(state: any, action: A) => void
    ) {

        return builder
    }
}

builder.addCase('test', (state, action: PayloadAction<number>) => {
    action.payload
}).addCase('tester', (state, action) => {
    action
})

您的使用方式,addCase() 应该是通用的,而 reducer 参数不是通用的,它只是使用 addCase().

中的类型参数

试试这个:

const builder = {
    addCase<A extends Action = AnyAction>(
        typeOrActionCreator: string,
        reducer: (state: any, action: A) => void
    ) {
        return builder
    }
}

您应该将泛型放在要推断类型的位置。在您调用 addCase() 的情况下,您会推断出有效载荷类型,然后 reducer 函数将始终使用该有效载荷类型。它已被锁定,您不能传递其他类型作为有效负载。这就是为什么 reducer 不是 通用函数。

Playground

您可以像这样使用 createAction 并传递有效载荷的类型:

const incrementByAmount = createAction<number, 'increment'>('increment')

现在,如果将鼠标悬停在 action 上,您将看到类型为 increment,有效负载为数字。

在 redux 工具包中,他们已经有一个如何设置有效负载类型的示例。 https://redux-toolkit.js.org/api/createreducer

来自 Redux 工具包文档的完整示例。

import { createAction, createReducer } from '@reduxjs/toolkit'

interface CounterState {
  value: number
}

const increment = createAction('counter/increment')
const decrement = createAction('counter/decrement')
const incrementByAmount = createAction<number>('counter/incrementByAmount')

const initialState = { value: 0 } as CounterState

const counterReducer = createReducer(initialState, (builder) => {
  builder
    .addCase(increment, (state, action) => {
      state.value++
    })
    .addCase(decrement, (state, action) => {
      state.value--
    })
    .addCase(incrementByAmount, (state, action) => {
      state.value += action.payload
    })
})