TypeScript 5.3.3中限制函数参数为映射类型衍生的数组类型键并解决编译问题
TypeScript 5.3.3中限制函数参数为映射类型衍生的数组类型键并解决编译问题
我懂你现在的困扰——你想把泛型K限制为T中值是数组类型的键,但当前代码在TS 5.3.3里编译不通过对吧?其实问题出在TypeScript对InsertableKeys<T>的类型推导上,虽然你定义了这个类型来筛选数组键,但编译器没办法自动关联K对应的T[K]一定是数组类型,导致调用some方法时因为类型不确定而报错。
先给你说清楚原代码的问题:你写的InsertableKeys<T>是通过映射类型把非数组的键设为never,再取[keyof T]得到符合条件的键的联合类型。但这种方式下,TS没办法反向推导“只要K属于InsertableKeys<T>,那T[K]就一定是数组”,所以在item[key].some这一行,编译器会质疑:item[key]会不会不是数组?有没有some方法?
下面给你两种可行的解决思路:
思路一:优化InsertableKeys的定义,让类型关联更明确
我们可以用TS的as子句改写InsertableKeys,直接只保留数组类型的键,同时在函数里用类型断言告诉编译器T[K]是数组:
// 只保留T中值为数组类型的键 export type InsertableKeys<T> = keyof { [U in keyof T as T[U] extends any[] ? U : never]: U } export const inserter = <T, K extends InsertableKeys<T>>( item: T, key: K, someFunc: (val: T[K][number]) => boolean ) => { // 因为K是InsertableKeys<T>,所以T[K]必然是数组,用类型断言消除编译报错 return (item[key] as Extract<T[K], any[]>).some(someFunc) }
思路二:直接在泛型约束中明确数组类型关联
这种方式不需要单独定义InsertableKeys,直接在函数泛型里约束T[K]必须是数组,同时加入运行时检查确保安全:
export const inserter = <T, K extends keyof T>( item: T, key: K, someFunc: (val: T[K][number]) => boolean ): T[K] extends any[] ? boolean : never => { // 运行时检查,避免类型断言带来的潜在风险 if (!Array.isArray(item[key])) { throw new Error(`Key "${String(key)}" does not correspond to an array property`) } return item[key].some(someFunc) as any }
两种方式都能实现你的需求:限制K只能是T中数组类型的键,同时让some方法调用通过编译。这里给你举个使用例子验证一下:
interface UserData { hobbies: string[] scores: number[] username: string } const user: UserData = { hobbies: ["reading", "coding"], scores: [85, 92, 78], username: "Symtox" } // 正常调用,返回true console.log(inserter(user, "hobbies", h => h === "coding")) // 正常调用,返回false console.log(inserter(user, "scores", s => s > 95)) // 编译报错,因为username不是数组类型的键 // inserter(user, "username", () => true)
备注:内容来源于stack exchange,提问作者Symtox
相关产品推荐
相关产品推荐

