我正在尝试使用immer https://github.com/mweststrate/immer作为我的减速器,但从打字稿中收到以下错误
Argument of type 'ReadonlyArray<IBidding>' is not assignable to parameter of type '(this: DraftArray<IBidding>, draftState: DraftArray<IBidding>, ...extraArgs: any[]) => void | Rea...'.
Type 'ReadonlyArray<IBidding>' provides no match for the signature '(this: DraftArray<IBidding>, draftState: DraftArray<IBidding>, ...extraArgs: any[]): void | ReadonlyArray<IBidding>'.
Run Code Online (Sandbox Code Playgroud)
我有types.ts这样的
export interface IBidding {
readonly id: string
readonly ownerId: string
readonly name: string
readonly description: string
readonly startDate: Date
readonly endDate: Date
readonly suppliers: ReadonlyArray<ISupplier>
readonly inquiryCreator: string
readonly bidStep: number,
readonly bids: ReadonlyArray<IBid>,
readonly startingBid: number,
readonly img: string
}
interface ISupplier {
id: string
name: string
}
interface IBid {
ownerId: string
value: number
createdAt: Date
}
export type IBiddingsState = ReadonlyArray<IBidding>
export const enum BiddingsActionTypes {
BID = '@@biddings/BID'
}
Run Code Online (Sandbox Code Playgroud)
这是我的reducer.ts
import { Reducer } from 'redux'
import produce from 'immer'
import { IBiddingsState, BiddingsActionTypes } from './types'
import { biddingsReducerInitialState as initialState } from './fixtures'
/**
* Reducer for biddings list
*/
const reducer: Reducer<IBiddingsState> = (state = initialState, action) => {
return produce<IBiddingsState>(state, draft => {
switch (action.type) {
case BiddingsActionTypes.BID:
const {
biddingId,
bid
} = action.payload
const biddingIndex = draft.findIndex(elem => elem.id === biddingId)
draft[biddingIndex].bids.push(bid)
return draft
default: {
return state
}
}
})
}
export { reducer as biddingsReducer }
Run Code Online (Sandbox Code Playgroud)
看来我按照文档中的说明做了所有事情,但仍然收到错误。为什么会发生这种情况?
不幸的是,当对produce, 之类的重载函数的调用与任何重载都不匹配时,TypeScript 很难猜测您打算用哪个重载来提供关于哪个参数错误的有意义的报告。如果您向配方添加一些类型注释:
const reducer: Reducer<IBiddingsState> = (state = initialState, action) => {
return produce<IBiddingsState>(state, (draft: Draft<IBiddingsState>): IBiddingsState => {
switch (action.type) {
case BiddingsActionTypes.BID:
const {
biddingId,
bid
} = action.payload
const biddingIndex = draft.findIndex(elem => elem.id === biddingId)
draft[biddingIndex].bids.push(bid)
return draft
default: {
return state
}
}
})
}
Run Code Online (Sandbox Code Playgroud)
然后你会发现问题出在 ,return draft并且你会得到更多信息:
[ts]
Type 'DraftArray<IBidding>' is not assignable to type 'ReadonlyArray<IBidding>'.
Types of property 'includes' are incompatible.
Type '(searchElement: DraftObject<IBidding>, fromIndex?: number) => boolean' is not assignable to type '(searchElement: IBidding, fromIndex?: number) => boolean'.
Types of parameters 'searchElement' and 'searchElement' are incompatible.
Type 'IBidding' is not assignable to type 'DraftObject<IBidding>'.
Types of property 'suppliers' are incompatible.
Type 'ReadonlyArray<ISupplier>' is not assignable to type 'DraftArray<ISupplier>'.
Property 'push' is missing in type 'ReadonlyArray<ISupplier>'.
Run Code Online (Sandbox Code Playgroud)
数组应该是协变的,为了支持这一点,第一个参数 是includes双变的,但不幸的是 TypeScript 猜测了错误的方向来报告失败。我们期望DraftObject<IBidding>可分配给IBidding,而不是相反。如果我们直接测试:
import { Draft } from 'immer'
import { IBidding } from './types'
let x: Draft<IBidding>;
let y: IBidding = x;
Run Code Online (Sandbox Code Playgroud)
然后我们终于看到了根本原因:
[ts]
Type 'DraftObject<IBidding>' is not assignable to type 'IBidding'.
Types of property 'startDate' are incompatible.
Type 'DraftObject<Date>' is not assignable to type 'Date'.
Property '[Symbol.toPrimitive]' is missing in type 'DraftObject<Date>'.
Run Code Online (Sandbox Code Playgroud)
这是因为DraftObject定义如下:
// Mapped type to remove readonly modifiers from state
// Based on https://github.com/Microsoft/TypeScript/blob/d4dc67aab233f5a8834dff16531baf99b16fea78/tests/cases/conformance/types/conditional/conditionalTypes1.ts#L120-L129
export type DraftObject<T> = {
-readonly [P in keyof T]: Draft<T[P]>;
};
Run Code Online (Sandbox Code Playgroud)
并且keyof不包含众所周知的符号,例如Symbol.toPrimitive(TypeScript问题)。
作为解决方法,您可以分叉immer类型并修改定义,Draft如下所示:
export type Draft<T> =
T extends any[] ? DraftArray<T[number]> :
T extends ReadonlyArray<any> ? DraftArray<T[number]> :
T extends Date ? Date : // <-- insert this line
T extends object ? DraftObject<T> :
T;
Run Code Online (Sandbox Code Playgroud)
或者,如果出现的次数不多,只需根据需要向代码中添加类型断言即可。
| 归档时间: |
|
| 查看次数: |
8769 次 |
| 最近记录: |