我希望能够创建一个函数工厂来更新Redux存储的子部分。在下面的示例中,编译器抱怨state[key]
可能是联合。有没有办法在运行时将K约束为"foo"
或"bar"
?
interface State {
foo: number[];
bar: string[];
}
const createUpdater = <K extends keyof State>(key: K) => (
state: State,
id: number,
updater: (subState: State[K][number]) => State[K][number]
) => {
const elements: State[K] = [...state[key]]; // <--- Error
elements[id] = updater(elements[id]);
return {
...state,
[key]: elements
};
};
const fooUpdater = createUpdater("foo");
const barUpdater = createUpdater("bar");
错误消息:
Type '(string | number)[]' is not assignable to type 'State[K]'.
Type '(string | number)[]' is not assignable to type 'number[] & string[]'.
Type '(string | number)[]' is not assignable to type 'number[]'.
Type 'string | number' is not assignable to type 'number'.
Type 'string' is not assignable to type 'number'.ts(2322)
答案 0 :(得分:2)
基于TypeScript Playground,这在3.5.1中是一个问题,但在3.6.3和ES7-beta中仅在ES3 / ES5目标中是 :ES2015及更高版本似乎不错。在这两种情况下,K都适当地约束为K extends "foo" | "bar"
。目标级别更改会导致类型推断错误,这一事实使我认为这是一个错误。
这可能与TS 3.5's "Smarter Union Type Checking"有关,但听起来与TS 3.6's "More Accurate Array Spread"更相关,尤其是考虑到ES5中生成的代码使用__spreadArray
。
作为一种解决方法,您可以使用类型断言来使TypeScript确信state[key]
可以被复制为State[K]
,但是正如“更精确的数组传播”部分slice
所述,语义与您的传播方式略有不同。
const elements = state[key].slice() as State[K];