TypeScript实现单数组入参下options的fields类型推导
TypeScript 单参数数组上下文类型推导实现问题
需求说明
我需要声明一个仅接收单个参数的函数,该参数为对象数组,每个对象包含name、options属性,以及可选的all属性,初始类型定义如下:
type Item = { name: string; all?: boolean; options: <Fields>(fields: Fields) => any[] } declare function useLink(items: Item[]): any
fields的类型需要满足以下约束:
Fields类型由数组中除当前对象外所有其他对象的name值映射生成- 若对象未设置
all或all为false,对应属性值类型为string[] - 若
all为true,对应属性值类型为string
期望的调用效果如下:
useLink( [ { name: 'a', all: true, options(fields: Fields) { // Fields类型为 { b: string, c: string, d: string } (排除自身a对应的string[]类型) return [] as any[] } }, { name: 'b', options(fields: Fields) { // Fields类型为 { a: string[], c: string, d: string } (排除自身b对应的string类型) return [] as any[] } }, { name: 'c', all: false, options(fields: Fields) { // Fields类型为 { a: string[], b: string, d: string } (排除自身c对应的string类型) return [] as any[] } }, { name: 'd', options(fields: Fields) { // Fields类型为 { a: string[], b: string, c: string } (排除自身d对应的string类型) return [] as any[] } } // ... 可扩展更多对象 ] )
现有实现(双参数版本)
目前仅实现了需要传入两个数组参数才能得到正确类型推导的方案,实现代码如下:
type LinkFields<All extends boolean, Name = string> = { name: Name; all?: All; [x: string]: any } /** * 示例 * WithOptions<[ { name: 'a', all: true }, { name: 'b' }, { name: 'c', all: false }, { name: 'd' } // ... 更多对象 ]> -> [ {options: (fields: {b: string, c: string, d: string}) => any[] }, {options: (fields: {a: string[], c: string, d: string}) => any[] }, {options: (fields: {a: string[], b: string, d: string}) => any[] }, {options: (fields: {a: string[], b: string, c: string}) => any[] }, // ... 更多对象 ] */ type WithOptions<T extends readonly any[], Fields = ArrayToObject<T>, R extends any[] = []> = T extends [] ? R : T extends [infer H, ...infer Rest] ? H extends { name: string } ? WithOptions<Rest, Fields, [...R, { options: (fields: Omit<Fields, H['name']>) => any[] }]> : never : R /** 用于推导元组类型而非普通数组类型 */ type TupleParams<T = any> = readonly T[] | readonly [T] type GetFieldValue<A> = A extends LinkFields<infer T> ? boolean extends T ? string : false extends T ? string : string[] : never; /** * 示例 * ArrayToObject<[ { name: 'a', all: true }, { name: 'b' }, { name: 'c', all: false }, { name: 'd' } // ... 更多对象 ]> -> {a: string[], b: string, c: string, d: string} */ type ArrayToObject<Arr extends TupleParams<LinkFields<any, any>> = []> = { [K in Arr[number]['name']]: GetFieldValue<Extract<Arr[number], { name: K }>> } declare function useLink<T extends TupleParams<LinkFields<All, Name>>, Name extends string, All extends boolean> (items: T, i?: WithOptions<T>): ArrayToObject<T> // 目前仅支持传入两个数组的调用方式 const a = useLink( [ { name: 'a', all: true }, { name: 'b', }, { name: 'c', all: false } // ... 更多对象 ], [ { options(fields) { // 当前fields类型为 { b: string, c: string } return [{ id: '', name: '' }].filter(item => fields.b) } }, { options(fields) { // 当前fields类型为 { a: string[], c: string } return [{ id: '', name: '' }].filter(item => fields.a.includes(item.id)) } }, { options(fields){ // 当前fields类型为 { a: string[], b: string } return [{ id: '', name: '' }].filter(item => fields.b === item.id) } } // ... 更多对象 ] )
目标效果
需要实现仅传入单个数组即可达成上述类型推导效果,不需要拆分两个参数传参,期望调用方式如下:
// 期望仅需传入单个数组的调用方式 const b = useLink( [ { name: 'a', all: true, options(fields) { // 当前fields类型为 { b: string, c: string } return [{ id: '', name: '' }].filter(item => fields.b) } }, { name: 'b', options(fields) { // 当前fields类型为 { a: string[], c: string } return [{ id: '', name: '' }].filter(item => fields.a.includes(item.id)) } }, { name: 'c', all: false, options(fields) { // 当前fields类型为 { a: string[], b: string } return [{ id: '', name: '' }].filter(item => fields.b === item.id) } } // ... 可扩展更多对象 ], )
内容的提问来源于stack exchange,提问作者Not one can help me
相关产品推荐
相关产品推荐

