【问题标题】:TypeScript types from array to object从数组到对象的 TypeScript 类型
【发布时间】:2021-12-04 17:40:48
【问题描述】:

我有一个数组,其中每个项目都是数组[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])
}

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

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.

【问题讨论】:

    标签: typescript


    【解决方案1】:

    如果数组是在编译时定义的,那么 typescript 将有机会推断类型。

    将内部元组转换为对象

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

    这将允许我们转换['getLength', (text: string) =&gt; text.length]
    { getLength: (text: string) =&gt; number }

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

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

    这将允许我们将数组数组转换为对象数组。
    我们现在可以通过querying the type of array itemArray[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]>;
    

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


    好的,让我们测试一下:

    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

    【讨论】:

    • 我尝试使用type TT = ToObject&lt;[x: string, y: number]&gt;;type TT = [x: string, y: number] 转换为type TT = {x: string, y: number},但它只产生type TT = { [x: string]: number; }。我做错了吗?
    • @danivicario 是的,你有完全不同的要求。你有一个标记的元组,相当于[string, number]
    【解决方案2】:

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

    在 typescript 中,ReturnTypeInstanceType 等高级类型只允许猜测已定义的类型。

    【讨论】:

      【解决方案3】:

      同样的问题已经困扰我好几天了。不过,对于 TypeScript@4.4.4,这可以通过自定义 typedef 实现:

      type ArrayToObject<Arr extends any[]> = {
          [Entry in keyof Arr as string]: Arr[Entry];
      };
      

      你可以这样使用:

      type FuncParamsToObj<Func extends (...args: any[]) => any> = ArrayToObject<Parameters<Func>>;
      
      type myFunc = ( a: string, b?: { x?: number, y?: object } ) => string;
      
      const paramObj: FuncParamsToObj<myFunc> = {
          a: 1,
          b: { x: 7, y: { w: 'hi' }},
      };
      

      【讨论】:

        猜你喜欢
        • 2023-04-01
        • 1970-01-01
        • 2022-08-19
        • 1970-01-01
        • 2020-10-28
        • 1970-01-01
        • 1970-01-01
        • 2021-10-24
        • 2016-10-14
        相关资源
        最近更新 更多