从数组到对象的 TypeScript 类型

Mic*_*hal 7 typescript

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

// 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])
}
Run Code Online (Sandbox Code Playgroud)

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

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.
Run Code Online (Sandbox Code Playgroud)

Ale*_* L. 11

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

将内部元组转换为对象

type ToObject<T> = T extends readonly [infer Key, infer Func]
  ? Key extends PropertyKey
  ? { [P in Key]: Func } : never : never;
Run Code Online (Sandbox Code Playgroud)

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

将元组数组转换为对象数组数组上的映射类型):

type ToObjectsArray<T> = {
  [I in keyof T]: ToObject<T[I]>
};
Run Code Online (Sandbox Code Playgroud)

这将允许我们将数组数组转换为对象数组。
我们现在可以通过查询数组项的类型 Array[number]来提取所需对象的并集。

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

type UnionToIntersection<U> =
  (U extends any ? (k: U) => void : never) extends ((k: infer I) => void) ? I : never;
Run Code Online (Sandbox Code Playgroud)

将所有内容组合在一起

// @ts-ignore
type FunctionMap<ArrayFunctions> = UnionToIntersection<ToObjectsArray<ArrayFunctions>[number]>;
Run Code Online (Sandbox Code Playgroud)

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


好的,我们来测试一下:

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'.
Run Code Online (Sandbox Code Playgroud)

操场