Typescript 从对象和键列表推断函数签名
我有一组表示为对象的指标:Typescript 从对象和键列表推断函数签名,typescript,mapped-types,Typescript,Mapped Types,我有一组表示为对象的指标: type Indicators = { UNIT_PRICE: number; QUANTITY: number; CURRENCY: "$" | "€"; TOTAL: string; }; 我想描述对这组指标进行的计算: type Computation<R extends { [key: string]: any }> = { output: keyof R; inputs: Arra
type Indicators = {
UNIT_PRICE: number;
QUANTITY: number;
CURRENCY: "$" | "€";
TOTAL: string;
};
我想描述对这组指标进行的计算:
type Computation<R extends { [key: string]: any }> = {
output: keyof R;
inputs: Array<keyof R>;
// I Would like the arguments and the returned value to be type checked
compute: (...inputs: any[]) => any;
};
这个答案是由一个名叫@webstrand的人在gitter上提供的 首先,映射类型可以表示
输出
、输入
和计算
签名之间的关系:
type Computation<
R extends { [key: string]: any },
// readonly is important to make typescript infer tuples instead of arrays later on
I extends readonly (keyof R)[],
O extends keyof R,
> = {
output: O;
inputs: I;
compute: (...inputs: { [P in keyof I]: I[P] extends keyof R ? R[I[P]] : never }) => R[O];
};
这个答案是由一个名叫@webstrand的人在gitter上提供的 首先,映射类型可以表示
输出
、输入
和计算
签名之间的关系:
type Computation<
R extends { [key: string]: any },
// readonly is important to make typescript infer tuples instead of arrays later on
I extends readonly (keyof R)[],
O extends keyof R,
> = {
output: O;
inputs: I;
compute: (...inputs: { [P in keyof I]: I[P] extends keyof R ? R[I[P]] : never }) => R[O];
};
为了实现这一点,您需要
计算
不仅在对象类型中是通用的,而且在输入键和输出键的类型中也是通用的。(从技术上讲,您可以尝试将“输入键和输出键的每一个可能元组”重新表示为一个大元组,但这表示起来很烦人,不能很好地扩展,因此我忽略了这种可能性。)例如:
type Computation<T extends Record<keyof T, any>,
IK extends (keyof T)[], OK extends keyof T> = {
output: OK;
inputs: readonly [...IK];
compute: (...inputs: {
[I in keyof IK]: T[Extract<IK[I], keyof T>]
}) => T[OK];
};
理想情况下,您希望编译器为您推断IK
和OK
,同时允许您指定T
。但目前TypeScript不允许您部分指定类型;您要么如上所述指定整个类型,要么让编译器为您推断整个类型。您可以创建一个helper函数,让编译器为您推断IK
和OK
,但同样,任何特定调用都只能推断所有T
、IK
和OK
,或者不推断任何一个。整个“部分推断”在打字稿中是一个公开的问题;请参阅以进行讨论
使用当前语言,我们能做的最好的事情是编写类似于泛型函数的东西,在初始函数上指定T
,然后让编译器在调用返回函数时推断IK
和OK
:
const asComputation = <T,>() =>
<IK extends (keyof T)[], OK extends keyof T>(c: Computation<T, IK, OK>) => c;
const asComputation=()=>
(c:计算)=>c;
它的工作原理如下:
const asIndicatorsComputation = asComputation<Indicators>();
const validComputation = asIndicatorsComputation({
output: "TOTAL",
inputs: ["UNIT_PRICE", "QUANTITY", "CURRENCY"],
compute: (unitPrice, quantity, currency) =>
(unitPrice * quantity).toFixed(2) + currency.toUpperCase(),
});
const wrongComputation = asIndicatorsComputation({
output: "TOTAL",
inputs: ["UNIT_PRICE"],
compute: (unitPrice) => unitPrice.toUpperCase() // error!
});
const asiindicatorscomputing=ascomputing();
常量有效计算=指示或计算({
输出:“总计”,
输入:[“单价”、“数量”、“货币”],
计算:(单价、数量、货币)=>
(单价*数量).toFixed(2)+currency.toUpperCase(),
});
常量错误计算=指示值计算({
输出:“总计”,
输入:[“单价”],
compute:(unitPrice)=>unitPrice.toUpperCase()//错误!
});
您可以调用asComputation()
来获取一个新的辅助函数,该函数接受编译器推断出的某些IK
和OK
的计算值。您可以看到,在compute()
方法参数是上下文类型的情况下,您得到了所需的行为,如果您做了错误的事情,您将得到一个错误
要使其正常工作,您需要计算
不仅在对象类型上是通用的,而且在输入键和输出键的类型上也是通用的。(从技术上讲,您可以尝试将“输入键和输出键的每一个可能元组”重新表示为一个大元组,但这表示起来很烦人,不能很好地扩展,因此我忽略了这种可能性。)例如:
type Computation<T extends Record<keyof T, any>,
IK extends (keyof T)[], OK extends keyof T> = {
output: OK;
inputs: readonly [...IK];
compute: (...inputs: {
[I in keyof IK]: T[Extract<IK[I], keyof T>]
}) => T[OK];
};
理想情况下,您希望编译器为您推断IK
和OK
,同时允许您指定T
。但目前TypeScript不允许您部分指定类型;您要么如上所述指定整个类型,要么让编译器为您推断整个类型。您可以创建一个helper函数,让编译器为您推断IK
和OK
,但同样,任何特定调用都只能推断所有T
、IK
和OK
,或者不推断任何一个。整个“部分推断”在打字稿中是一个公开的问题;请参阅以进行讨论
使用当前语言,我们能做的最好的事情是编写类似于泛型函数的东西,在初始函数上指定T
,然后让编译器在调用返回函数时推断IK
和OK
:
const asComputation = <T,>() =>
<IK extends (keyof T)[], OK extends keyof T>(c: Computation<T, IK, OK>) => c;
const asComputation=()=>
(c:计算)=>c;
它的工作原理如下:
const asIndicatorsComputation = asComputation<Indicators>();
const validComputation = asIndicatorsComputation({
output: "TOTAL",
inputs: ["UNIT_PRICE", "QUANTITY", "CURRENCY"],
compute: (unitPrice, quantity, currency) =>
(unitPrice * quantity).toFixed(2) + currency.toUpperCase(),
});
const wrongComputation = asIndicatorsComputation({
output: "TOTAL",
inputs: ["UNIT_PRICE"],
compute: (unitPrice) => unitPrice.toUpperCase() // error!
});
const asiindicatorscomputing=ascomputing();
常量有效计算=指示或计算({
输出:“总计”,
输入:[“单价”、“数量”、“货币”],
计算:(单价、数量、货币)=>
(单价*数量).toFixed(2)+currency.toUpperCase(),
});
常量错误计算=指示值计算({
输出:“总计”,
输入:[“单价”],
compute:(unitPrice)=>unitPrice.toUpperCase()//错误!
});
您可以调用asComputation()
来获取一个新的辅助函数,该函数接受编译器推断出的某些IK
和OK
的计算值。您可以看到,在compute()
方法参数是上下文类型的情况下,您得到了所需的行为,如果您做了错误的事情,您将得到一个错误
您是否需要输出
和输入
作为实际属性?如果将它们指定为计算的类型参数
,则解决方案相当简单。否则,它会变得很棘手,因为您需要推断一些类型参数,但要显式指定指示符
。Typescript无法推断指示符
中键的顺序。因此,充其量,您的输入将是数组
,而不是元组[number,number,“$”|“€”]
。您仍然需要在compute函数中输入typeguard/assert。您的指标
类型实际上是否对应于运行时对象,或者这正是您目前所研究的吗?@Oblosys我确实需要它们成为实际属性。用例是const updatedata=applycompution(computing,currentData)
,其中applycompution
调用compute
,并提取f