【问题标题】:How to narrow TypeScript union type with generics如何使用泛型缩小 TypeScript 联合类型
【发布时间】:2021-06-09 01:11:15
【问题描述】:

我有一个通用函数来更新状态(用例是动态处理 React 中表的更新),并使用泛型来确保调用该函数是类型安全的,但我不明白为什么 TypeScript 不会编译。

在函数本身中,Typescript 似乎没有使用所有可用信息来缩小类型,而是认为我仍在使用完整的联合。 它清楚地知道足够的参数来判断函数是否被正确调用,那么为什么在对实际实现进行类型检查时会失败?

row[field] = value 失败。

简单示例:

type First = { a: string; b: string; c: string }
type Second = { b: string; c: string; d: string }
type Third = { c: string; d: string; e: string }

type State = {
  first: First[]
  second: Second[]
  third: Third[]
}

const update = <
  S extends State,
  TK extends keyof State,
  T extends S[TK],
  R extends T[number],
  F extends keyof R
>(
  state: State,
  tagName: TK,
  rowIndex: number,
  field: F,
  value: R[F]
) => {
  // fine
  const tag = state[tagName]

  // fine
  const row = tag[rowIndex]
  // keyof typeof row is now 'c'

  // TYPE ERROR
  row[field] = value
}

const state: State = {
  first: [{ a: "", b: "", c: "" }],
  second: [{ b: "", c: "", d: "" }],
  third: [{ c: "", d: "", e: "" }],
}

// this succeeds as expected
update(state, "first", 0, "a", "new")

// and this fails as expected
// @ts-expect-error
update(state, "second", 0, "a", "new")

Playground

【问题讨论】:

    标签: typescript typescript-generics union-types


    【解决方案1】:

    试试这个:

    const update = <
      TK extends keyof State,
      F extends keyof State[TK][number]
    >(
      state: State,
      tagName: TK,
      rowIndex: number,
      field: F,
      value: State[TK][number][F]
    ) => {
      // fine
      const tag = state[tagName]
    
      // fine
      // The type annotation here is required
      // (otherwise the type is inferred as First | Second | Third)
      const row: State[TK][number] = tag[rowIndex]
    
      // now also fine
      row[field] = value
    }
    

    【讨论】:

    • 谢谢,完美运行。请注意,这需要两阶段 const row: ... 部分并将 F 的类型更改为不仅仅是 keyof R。不知道为什么 TS 无法解决这个问题,但请记住
    猜你喜欢
    • 2021-02-26
    • 1970-01-01
    • 1970-01-01
    • 2022-10-02
    • 1970-01-01
    • 2020-07-31
    • 1970-01-01
    • 2021-06-05
    • 2020-04-12
    相关资源
    最近更新 更多