You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 09:09:56