如何使用泛型缩小TypeScript联合类型

5

我有一个通用函数来更新状态(场景是在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

使用 Playground 来尝试 TypeScript 代码。
1个回答

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 无法解决这个问题,但记住这一点很好。 - Sam Woolerton

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接