如何在运行时将并集限制为一种给定类型?

时间:2019-10-10 17:20:32

标签: typescript typescript-typings

我希望能够创建一个函数工厂来更新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)

1 个答案:

答案 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];