如何限制联合字面量类型并通过类型谓词实现find正确类型推导
解决泛型联合类型数组filter/find的类型谓词报错问题
问题背景
需要实现一个类型谓词,让泛型联合类型数组调用filter或find时返回精确的子类型。先定义了以下类型,用于映射不同id对应的转换函数输入输出:
type ConverterGroupIdToFunc = { text: [string, string] image: [File, string] } type ConverterGroupId = keyof ConverterGroupIdToFunc type ConverterGroup<T extends ConverterGroupId> = { id: T convertFunc: (s: ConverterGroupIdToFunc[T][0]) => ConverterGroupIdToFunc[T][1]; } type ConverterGroupUnion = ConverterGroup<"text"> | ConverterGroup<"image">
尝试编写类型谓词函数时触发TS2677错误:
function isConverterGroup<T extends ConverterGroupId>(converterGroupId: T) { return (converterGroup: ConverterGroupUnion): converterGroup is ConverterGroup<T> => { return converterGroup.id === converterGroupId; } } const convertFunc = converterGroups.find(isConverterGroup("text"))!.convertFunc;
报错信息:
A type predicate's type must be assignable to its parameter's type. Type 'ConverterGroup<T>' is not assignable to type 'ConverterGroupUnion'. Type 'ConverterGroup<T>' is not assignable to type 'ConverterGroup<"text">'. Types of property 'id' are incompatible. Type 'T' is not assignable to type '"text"'.ts(2677)
报错原因
TypeScript无法确认泛型T对应的ConverterGroup<T>一定是ConverterGroupUnion的子类型——虽然T属于ConverterGroupId,但类型谓词要求断言的类型必须能赋值给参数类型(即ConverterGroupUnion),直接使用ConverterGroup<T>无法满足该约束。
解决方案
使用Extract工具类型从联合类型中提取匹配id: T的成员,让TypeScript明确断言的类型是ConverterGroupUnion的子类型:
function isConverterGroup<T extends ConverterGroupId>(converterGroupId: T) { return (converterGroup: ConverterGroupUnion): converterGroup is Extract<ConverterGroupUnion, { id: T }> => { return converterGroup.id === converterGroupId; } }
验证示例
const converterGroups: ConverterGroupUnion[] = [ { id: "text", convertFunc: (s: string) => s.toUpperCase() }, { id: "image", convertFunc: (f: File) => URL.createObjectURL(f) } ]; // 自动推断类型为 ConverterGroup<"text"> const textConverter = converterGroups.find(isConverterGroup("text"))!; const textFunc = textConverter.convertFunc; // 类型:(s: string) => string // 自动推断类型为 ConverterGroup<"image"> const imageConverter = converterGroups.find(isConverterGroup("image"))!; const imageFunc = imageConverter.convertFunc; // 类型:(s: File) => string
内容的提问来源于stack exchange,提问作者SiAce
相关产品推荐
相关产品推荐

