我有一个代表我的应用程序中的操作的类型:
type Operation<A, B> = {
execute: (data: A) => B;
};
操作如下所示:
const op = {
execute: (data: string) => {
return data;
},
};
我正在尝试创建一个抽象来装饰这个操作。添加装饰应保留任何先前添加的字段。例如,当我想通过事务处理来增强操作时:
export const wrapInTransaction = <A, B, O extends Operation<A, B>>(
{ execute, ...rest}: O
): O => {
const wrappedExecute = (data: A) =>
{
// add transaction logic here
return execute(data)
}
return {
...rest,
execute: wrappedExecute,
} as O;
}
我接受
Operation
的子类型,而不是通用 Operation<A, B>
。我的问题是这不起作用:
const wrapped = wrapInTransaction(op);
// Argument of type '{ execute: (data: string) => string; }' is not assignable to parameter // of type 'Operation<unknown, unknown>'.
// Types of property 'execute' are incompatible.
// Type '(data: string) => string' is not assignable to type '(data: unknown) => unknown'.
// Types of parameters 'data' and 'data' are incompatible.
// Type 'unknown' is not assignable to type 'string'
因为
O
的类型未被推断出来。我可以以某种方式构造 O
的默认值来推断 A
和 B
吗?
我建议简化通用性。鉴于它仅将
O
应用于参数 { execute, ...rest}: O
,它只能推断 O
。虽然推导 A
和 B
对于人类来说是微不足道的,但我们可以使用此处记录的 infer
关键字严格地完成:条件类型,以帮助我们获得 A
和 B
。请注意,它实际上不能在泛型中,否则它是循环类型依赖。
// VV this works as well
// type DataFromOperation<T> = T extends Operation<infer A, unknown> ? A : unknown
type DataFromOperation<T extends Operation<unknown, unknown>> = T extends Operation<infer A, unknown> ? A : unknown
export const wrapInTransaction = <O extends Operation<any, any>>(
{ execute, ...rest}: O
): O => {
const wrappedExecute = (data: DataFromOperation<O>) =>
{
// add transaction logic here
return execute(data)
}
return {
...rest,
execute: wrappedExecute,
} as O;
}