TypeScript 遍历对象并根据值创建类型

TypeScript Iterate over object and create a type from the values

鉴于我有这样一个对象:

const props = [
    { name: 'car', list: { brand: 'audi', model: 's4' }
    { name: 'motorcycle', list: { type: 'ktm', color: 'orange' }
] as constant;

我想创建一个代表如下内容的类型:

type Props = {
    car?: 'brand' | 'model',
    motorcycle?: 'type' | 'color'
};

我得到的最接近的是这样的:

type Props = Partial<Record<typeof props[number]['name'], typeof props[number]['list']>

但是returns是这样的:

type Props = {
    car?: { brand: 'audi', model: 's4' } | { type: 'ktm' | color: 'orange' } | undefined,
    motorcycle?: { brand: 'audi', model: 's4' } | { type: 'ktm' | color: 'orange' } | undefined
}

怎样才能达到预期的效果?

您可以使用 TypeScript 4.1 (docs) 中引入的 键重映射 来实现此目的:

type IndexKeys<T> = Exclude<keyof T, keyof []>
type ListProp = { name: string, list: object }
type GetName<P> = P extends ListProp ? P['name'] : never
type GetListKeys<P> = P extends ListProp ? keyof P['list'] : never

type PropsFromList<PropList extends ReadonlyArray<ListProp>> = {
  [i in IndexKeys<PropList> as GetName<PropList[i]>]?: GetListKeys<PropList[i]>
}

type Props = PropsFromList<typeof props>
// Inferred type:
// Props: {
//     car?: "brand" | "model" | undefined;
//     motorcycle?: "type" | "color" | undefined;
// }

请注意,就像使用 Partial 时一样,可选的 属性 类型会获得额外但无害的 | undefined

TypeScript playground