我一直在尝试将Typescript的推理引擎最大程度地发挥我正在开发的实用程序的作用,但是,我一直在努力使其能够按需运行。
通用util接收一组“映射器”功能作为第一个参数,并接收“ transformer”功能作为第二个参数。
“映射器”功能的结果将成为“变压器”功能的输入。
我想根据“映射器”解析的数据将参数类型动态地推导到“ transformer”函数。
我已经创建了以下基本说明:
type Transformation<
Data extends object = {},
Result = any,
Mappers extends Array<(data: Data) => any> = Array<(data: Data) => any>
> = (
mappers: Mappers,
transform: (
// Over here I am trying to infer out the results of my mapper, with this
// implementation supporting up to 2 mappers
...args: Mappers extends [(data: Data) => infer Arg1]
? [Arg1]
: Mappers extends [(data: Data) => infer Arg1, (data: Data) => infer Arg2]
? [Arg1, Arg2]
: any[]
) => Result,
) => void;
interface Person {
name: string;
age: number;
}
const personTransformer = {} as Transformation<Person, string>;
personTransformer(
// mappers:
[person => person.name, person => person.age],
// | |
// |---------------| |
// | |
// | |----------------------------------
// | |
(name, age) => `${name} is ${age} years old`,
);
Click here for the TypeScript playground of this
我希望将转换器函数动态地推断为(string, number) => string
,但是将其键入为(any, any) => string
。
有什么办法可以让TypeScript进行这种推断?
答案 0 :(得分:0)
我认为这里的根本问题是,您希望通过为Mappers
使用generic parameter default(我将其称为M
),以某种方式来推断它。不幸的是,它将仅使用默认值,而不是尝试在任何地方进行推断。因此,您要指定Data
(对我来说是D
)和Result
(对我来说就是R
),并获得使用所有M
的默认any
在这个地方。
本质上,此修复程序是使Transformation<D, R>
本身成为依赖于类型M
的泛型函数。因此,您拥有type Transformation<D, R, M> = ()=>{}
而不是type Transformation<D, R> = <M>()=>{}
。这样就可以从M
的使用中推断出它。 (就我所知,您应该将其设置为type Transformation = <D, R, M>()=>{}
,并由Transformation
的调用者指定所有三个通用参数,但这与您的用例有关。)
这就是我要对其进行定义的方式:
// less restrictive form of ReturnType<T>
type Ret<T> = T extends (...args: any) => infer R ? R : never;
type Transformation<D, R> = <M extends ReadonlyArray<(data: D) => any>>(
mappers: M | [], // | [] hints that mappers should be a tuple type
transform: (...args: { [K in keyof M]: Ret<M[K]> }) => R
) => (data: D) => R; // I assume you want to actually transform things
如果您希望(data: D) => R
像您的版本一样工作,则可以将其替换为void
。我只是认为Transformation<D, R>
应该产生实际上将D
转换为R
的东西才有意义。
使用M | []
代替M
,因为它充当hint来将mappers
参数解释为元组类型而不是数组...并且您需要这样做,以便严格按顺序键入transform
的参数(而不仅仅是一大堆东西)。
请注意,transform
函数在mappers
中对应于函数返回类型的mapped tuple上进行操作。
我们也可以在运行时而不是使用{}
来实现:
const transformer = <D, R>() =>
(((mappers: ((d: D) => any)[], transform: (...a: any[]) => R) => (data: D) =>
transform(...mappers.map(m => m(data)))) as any) as Transformation<D, R>;
这必须使用一些类型断言,但是它基本上将mappers
中的每个函数应用于data
,结果数组散布到transform()
中。
最后,我们可以使用上面的内容获得Transformation<Person, string>
:
const personTransformer = transformer<Person, string>();
我们看到它的行为与您期望的一样
const p = personTransformer(
[person => person.name, person => person.age],
(name, age) => name.toUpperCase() + " IS " + age.toFixed(1) + " YEARS OLD"
);
已知name
和age
分别是string
和number
类型的地方。
我在上面给出的实现结果如下:
console.log(p({ name: "Billy", age: 9 }));
// BILLY IS 9.0 YEARS OLD
好的,希望能有所帮助;祝你好运!