首页 > 解决方案 > 将对象列表转换为记录类型

问题描述

我正在尝试编写一个类型安全的函数,它接收一个 shape 对象列表{ key: Key, test: boolean }并返回一个 shape 对象{ [name: Key]: Type }Key应该只是extends stringType应该是number如果test给定对象的属性是true并且string如果它是false.

例子:

const r = f([
    { key: 'foo', test: true },
    { key: 'bar', test: false }
]);

// `typeof r` should be `{ 'foo': number, 'bar': string }`

这是我能走多远,我不知道之后该怎么做。

function f<T extends string>(xs: { name: T, key: boolean }[]): ??? {
    // ...
}

这样的事情甚至可能吗?

标签: typescript

解决方案


这是可以实现的。完整解决方案:

// type represents the input array element
type KeyTest = { key: string, test: boolean };

// type process input array into wanted object
type Result<T extends KeyTest[]
    ,_Keys extends string = {
        [K in keyof T]: T[K] extends KeyTest ? T[K]['key'] : never
    }[keyof T & number]> = {
    [K in _Keys]: {
            [I in keyof T]:
            T[I] extends KeyTest ? T[I]['key'] extends K
            ? true extends T[I]['test'] ? number : string
            : never
            : never
    }[keyof T & number]
} 

// type level Result type check
type Test = Result<[
    { key: 'foo', test: true },
    { key: 'bar', test: false }
]>

declare function f<T extends Array<KT>
, KT extends {key: Key, test: Test}
, Key extends string
, Test extends boolean>(...xs: T): Result<T>

// example usage:    
const r = f({ key: 'foo', test: true },  { key: 'bar', test: false });
type X = typeof r;

// working with existing arrays
const arr = [
    { key: 'foo', test: true },
    { key: 'bar', test: false }
] as const; // here as const because type will be infered as an array
const r2 = f(...arr);
type X2 = typeof r2;

游乐场链接

不幸的是,为了获得适当的类型缩小,我需要将函数输入从单个 Array 参数更改为参数列表。我希望这对你来说不是问题。真正的区别是我们需要...在传递数组之前使用。这样做是因为默认情况下 TS 将数组类型视为数组而不是元组。

类型说明Result

  • _Keys- 这些都是key从元组中收集的所有属性类型
  • [K in _Keys]- 我们正在遍历从元组收集的所有键
  • [I in keyof T]- 我们再次遍历原始元组,就像 for inside for
  • T[I]['key'] extends K ? true extends T[I]['test'] ? number : string- 所以我们在第二个里面,我们检查元组元素是否具有key我们在地图类型中拥有的确切当前键,如果是,我们检查是否test为真,如果是,如果number不是,我们分配给它string

推荐阅读