typescript 泛型数组中如何根据某个字段类型约束另一个字段的类型?

需求类似json表单,通过json数组配置来实现一些功能。
比如https://segmentfault.com/q/1010000044539938,但是它这个是单项配置。

有如下ts代码:

type Component = Record<string, any>

const input: { readonly?: boolean } = {} satisfies Component
const select: { options?: any[] } = {} satisfies Component

const components = { input, select } as const satisfies Record<string, Component>

type DefinedComponents = typeof components
type DefinedComponentType = keyof DefinedComponents

type Item<Type extends (DefinedComponentType | Component)> = {
  type: Type,
  props: Type extends DefinedComponentType ? DefinedComponents[Type] : Type
}

function createItem<Type extends (DefinedComponentType | Component)>(item: Item<Type>) {}

createItem({ type: 'input', props: { readonly: false } })
createItem({ type: {} as { customProps?: number }, props: { customProps: 0 } })

可以看到对于单项配置是没问题的,然后想实现数组配置:

function createItems<Type extends (DefinedComponentType | Component)>(items: Item<Type>[]) {}

createItems([
  { type: 'input', props: { readonly: false } },
  { type: 'select', props: { options: [] } }
])

这里 createItems 中的 props 会推断成 { readonly?: boolean } | { options?: any[] },也就是 inputselect 的联合类型,这也好理解,因为泛型 Type 现在是 'input' | 'select'

那要怎么修改 createItems 的参数类型,才能实现 type'input' 时,props 能正确推断为 { readonly?: boolean }'select' 或者自定义 type 同理?

阅读 503
avatarAI BotBETA

要实现数组类型中每个元素独立绑定 typeprops 的关系,需要使用映射类型处理元组。修改后的代码如下:

type Map<T extends readonly any[]> = {
  [K in keyof T]: T[K] extends { type: infer Type } 
    ? Item<Type & (DefinedComponentType | Component)> 
    : never
}

function createItems<T extends { type: any }[]>(items: [...Map<T>]) {}

// 测试用例:
createItems([
  { type: 'input', props: { readonly: false } },         // props 类型正确推断为 { readonly?: boolean }
  { type: 'select', props: { options: [] } },            // props 类型正确推断为 { options?: any[] }
  { type: { custom: 1 }, props: { custom: 2 } }          // 自定义类型正常工作
])

关键修改点:

  1. Map 类型将遍历数组元组,为每个元素单独应用 Item<Type> 类型
  2. 使用可变元组类型 [...T] 保持数组元素的独立性
  3. 通过 T extends { type: any }[] 约束确保元素结构正确

这种方法会保持数组元素的顺序和独立类型关系,而不是将整个数组的类型合并为联合类型。每个数组元素都会单独检查其 typeprops 的对应关系。

1 个回答
✓ 已被采纳
type ItemType<T> = T extends DefinedComponentType
  ? { type: T; props: DefinedComponents[T] }
  : T extends Component
  ? { type: T; props: T }
  : never;

function createItems<Items extends Array<Component | DefinedComponentType>>(
  items: {
    [K in keyof Items]: ItemType<Items[K]>
  }
) {
  return items;
}

createItems([
  { type: 'input', props: { readonly: false } },
  { type: 'select', props: { options: [] } },
  { type: {}as { foo?: number }, props: {foo: 123}}
])

playground

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题