TypeScript Generic Factory Function Type,匹配数组元素顺序

Ben*_*n M 5 generics factory-method typescript typescript-generics

我想构建某种FactoryFactory:基本上是一个返回工厂函数的通用函数。编写函数本身很简单,但我不知道如何为它做 TypeScript 类型。

该函数应该像这样使用:

const stubFactoryFunction = (...props) => (...values) => ({ /* ... */ });

const factory = stubFactoryFunction("prop1", "prop2");
const instance = factory("foo", 42);
console.log(instance); // { prop1: "foo", prop2: 42 }
Run Code Online (Sandbox Code Playgroud)

起初,我尝试将值类型作为数组提供:

type FactoryFunction<T extends any[]> =
  <K extends string[]>(...props: K) =>
    (...values: T[number]) =>
      {[key in K[number]]: T[number]}
Run Code Online (Sandbox Code Playgroud)

但这将导致{ prop1: string | number, prop2: string | number},因为类型与数组索引不匹配。

接下来,我尝试将整个对象提供为泛型类型:

type FactoryFunction<T extends {[key: string]: any}> =
  (...props: (keyof T)[]) =>
    (...values: ???) =>
      T
Run Code Online (Sandbox Code Playgroud)

在这里我遇到了类似的问题:values必须以某种方式匹配props.

这可能吗?

奖励 1:不允许重复道具。

奖励 2:强制从T.

Rom*_*eau 5

这是另一个解决方案,这次是通用的,并且最终没有那么复杂,尽管一些中间类型是必要的。基本想法是:

  • 具有字符串元组类型P(对于“Props”)中的键的对象类型 ( extends string[]) 是通过映射类型完成的{ [K in P[number]]: ... }
  • K困难在于获取 中(“Props”之一)的索引P。它是使用另一个映射类型完成的IndexOf,而该类型本身又使用第三个映射类型Indexes
  • 然后,对象类型值由 给出,仅当是(对于“值”)的索引V[IndexOf<P, K>]时才可以接受,因此是条件类型。我们永远不会有这样的情况,因为由于约束,和数组类型具有相同的长度。IndexOf<P, K>VIndexOf<P, K> extends keyof V ? V[IndexOf<P, K>] : neverneverPVV extends (any[] & { length: P['length'] })
// Utility types
type Indexes<V extends any[]> = {
  [K in Exclude<keyof V, keyof Array<any>>]: K;
};

type IndexOf<V extends any[], T> = {
  [I in keyof Indexes<V>]: V[I] extends T ? T extends V[I] ? I : never : never;
}[keyof Indexes<V>];

type FactoryFunctionResult<P extends string[], V extends (any[] & { length: P['length'] })> = {
  [K in P[number]]: IndexOf<P, K> extends keyof V ? V[IndexOf<P, K>] : never;
};

// Tests
type IndexesTest1 = Indexes<['a', 'b']>; // { 0: "0"; 1: "1" }

type IndexOfTest1 = IndexOf<['a', 'b'], 'b'>; // "1"
type IndexOfTest2 = IndexOf<['a', 'b'], string>; // never
type IndexOfTest3 = IndexOf<[string, string], 'a'>; // never
type IndexOfTest4 = IndexOf<[string, string], string>; // "0" | "1"

type FactoryFunctionResultTest1 = FactoryFunctionResult<['a'], [string]>; // { a: string }
type FactoryFunctionResultTest2 = FactoryFunctionResult<['a', 'b'], [string, number]>; // { a: string; b: number }
type FactoryFunctionResultTest3 = FactoryFunctionResult<['a', 'b', 'c'], [string, number, boolean]>; // { a: string; b: number; c: boolean }
type FactoryFunctionResultTest4 = FactoryFunctionResult<['a', 'b', 'c', 'd'], [string, number, boolean, string]>; // { a: string; b: number; c: boolean; d: string }
Run Code Online (Sandbox Code Playgroud)