我正在尝试将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>'.
我有这样的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'
}
这是我的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 }
似乎我做了文档中的所有操作,但仍然收到错误。为什么会这样?
答案 0 :(得分:1)
不幸的是,当对诸如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
}
}
})
}
然后您发现问题出在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>'.
数组应该是协变的,为此,includes
的第一个参数是双变量的,但是不幸的是TypeScript猜测报告错误的方向是错误的。我们期望DraftObject<IBidding>
可分配给IBidding
,而不是相反。如果我们直接进行测试:
import { Draft } from 'immer'
import { IBidding } from './types'
let x: Draft<IBidding>;
let y: IBidding = x;
然后我们终于看到了根本原因:
[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>'.
这是因为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]>;
};
并且keyof
不包含诸如Symbol.toPrimitive
(TypeScript issue)之类的众所周知的符号。
作为解决方法,您可以分叉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;
或者,如果发生的次数不多,只需在代码中添加类型断言即可。
答案 1 :(得分:1)
您并不孤单! Immer已更新,希望能在这种情况下为您提供帮助。这是更改:
https://github.com/mweststrate/immer/commit/512256bbde4ea1e2b6a75399d6ad59925752ad6b
只要您使用TypeScript 2.8.x或更高版本,就应该不错。
此更改自沉浸式1.7.4开始生效。
答案 2 :(得分:0)
我今天有一个类似的问题,我正在沉浸在7.0.9中。在文档中查找后,他们确实提到了实用程序功能castDraft
作为解决方法。
https://immerjs.github.io/immer/docs/typescript#cast-utilities