如何使用泛型缩小 TypeScript 联合类型
Posted
技术标签:
【中文标题】如何使用泛型缩小 TypeScript 联合类型【英文标题】:How to narrow TypeScript union type with generics 【发布时间】: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
【问题讨论】:
【参考方案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 无法解决这个问题,但请记住以上是关于如何使用泛型缩小 TypeScript 联合类型的主要内容,如果未能解决你的问题,请参考以下文章