从数组到对象的TypeScript类型

时间:2020-03-26 07:19:29

标签: typescript

我有一个数组,其中每个项目都是数组[name: string, someFunction: Function]。我想将其转换为键为name且值为someFunction s的对象:

// Input
const arrayFunctions = [
    ['getLength', (text: string) => text.length],
    ['setValue', (id: string, value: number) => {}],
    ['getAll', () => ([1, 2, 3])]
]

// Output
const objectFunctions = {
    getLength: (text: string) => text.length,
    setValue: (id: string, value: number) => {},
    getAll: () => ([1, 2, 3])
}

有什么方法可以连接输入数组中的函数类型和输出对象中的函数类型?

type ObjectFunctions<ArrayFunctions> = { [/* Value from ArrayFunctions[i][0] */]: /* Value from ArrayFunctions[i][1] */ }

const arrayToObject = <ArrayFunctions extends Array<any>>(functions: ArrayFunctions) => {
    const result = {}

    for (const [name, func] of functions) {
        result[name] = func
    }

    return result as ObjectFunctions<ArrayFunctions>
}

const arrayFunctions = [
    ['getLength', (text: string) => text.length],
    ['setValue', (id: string, value: number) => {}],
    ['getAll', () => ([1, 2, 3])]
]

const objectFunctions = arrayToObject(arrayFunctions)

const length = objectFunctions.getLength() // Should be error because first parameter (text) is missing.
objectFunctions.setValue(true, 2) // Should be error, because of first parameter (id) must be string.

2 个答案:

答案 0 :(得分:3)

如果数组是在编译时定义的,则打字稿将有机会推断类型。

将内部元组转换为对象

type ToObject<T> = T extends readonly [infer Key, infer Func]
  ? Key extends PropertyKey
  ? { [P in Key]: Func } : never : never;

这将使我们能够转换['getLength', (text: string) => text.length]
{ getLength: (text: string) => number }

将元组数组转换为对象数组mapped type on array):

type ToObjectsArray<T> = {
  [I in keyof T]: ToObject<T[I]>
};

这将使我们能够将数组数组转换为对象数组。
现在,我们可以通过querying the type of array item Array[number]提取所需对象的并集。

最后一步-我们实际上需要交集而不是联合。我们可以使用著名的UnionToIntersection

type UnionToIntersection<U> =
  (U extends any ? (k: U) => void : never) extends ((k: infer I) => void) ? I : never;

组合在一起

// @ts-ignore
type FunctionMap<ArrayFunctions> = UnionToIntersection<ToObjectsArray<ArrayFunctions>[number]>;

忽略以上需要,因为打字稿在数组类型上使用映射类型时会忘记它会生成数组。


好,我们来测试:

const arrayToObject = <ArrayFunctions extends ReadonlyArray<any>>(functions: ArrayFunctions) => {
  const result: any = {}

  for (const [name, func] of functions) {
    result[name] = func
  }

  return result as FunctionMap<ArrayFunctions>
}

const arrayFunctions = [
  ['getLength', (text: string) => text.length],
  ['setValue', (id: string, value: number) => { }],
  ['getAll', () => ([1, 2, 3])]
] as const;

const objectFunctions = arrayToObject(arrayFunctions);

const l = objectFunctions.getLength() // Expected 1 arguments, but got 0
objectFunctions.setValue(true, 2) // Argument of type 'true' is not assignable to parameter of type 'string'.

Playground

答案 1 :(得分:0)

您不能,Typescript编译器无法动态猜测类型(在运行时)。

在打字稿中,仅允许ReturnTypeInstanceType之类的高级类型猜测已经定义的类型。