从包装函数推断包装器的类型
Infer the type of a wrapper from wrapped function
objective是从wrapped function推断出wrapper的类型,即return从参数类型推断出wrapper的类型
我无法通过 infer
实现:
function wrap<T extends ((...args: any[]) => any) & { flag?: string }>(wrapped: T extends infer R ? R : never): T {
let wrapper = function () {
// ...
return wrapped(...arguments);
} as T;
wrapper.flag = wrapped.flag;
return wrapper;
}
let wrapped = (a: number, b: number) => `${a}{b}`;
// wrapper is ((...args: any[]) => any) & { flag?: string }
// should be ((a: number, b: number) => string & { flag?: string }
let wrapper = wrap(wrapped);
// foo is any
// should be string
let foo = wrapper('invalid');
在这种情况下如何在不明确指定包装器类型的情况下完成此操作?
给定类型 T extends infer R ? R : never
的值,编译器几乎肯定无法推断出对 T
有用的任何内容(因为它倾向于推迟评估这种条件类型,直到为时已晚T
进行推断)。由于该类型本质上等同于 T
,因此我们将在下文中使用 T
。
我认为 wrap()
签名和实现应该是这样的:
function wrap<T extends ((...args: any[]) => any)>(
wrapped: T & { flag?: string }): T & { flag?: string } {
let wrapper = function () {
// ...
return wrapped(...arguments);
} as T & { flag?: string };
wrapper.flag = wrapped.flag;
return wrapper;
}
也就是说,我们将只使用通用参数 T
来表示函数类型,而将 {flag?: string}
作为单独的非通用对象类型保留 T
将相交。您需要这样做的原因与可选属性的可分配性规则有关:
在 TypeScript 中,对象类型 缺少 属性 被认为可分配给具有 可选 [=54] 的对象=] 任何类型。例如,类型 {foo: string}
可分配给类型 {foo: string, bar?: number}
。如果你有一个通用类型 T extends {foo: string, bar?: number}
,T
可能是 {foo: string}
,完全没有 bar
属性。
在你的例子中,如果你有一个通用类型 T extends ((...args: any[])=>any) & {flag?: string}
,并传入一个没有已知 flag
属性 的函数作为 T
的推理站点,编译器将推断 T
只是没有 flag
属性 的函数类型。然后函数输出也没有flag
属性,郁闷
为了确保编译器将函数的输入和输出解释为具有可选的 flag
属性,我们需要在输入和输出中显式包含它,并且不要让编译器有机会忘记它。
好的;希望有所帮助;祝你好运!
objective是从wrapped function推断出wrapper的类型,即return从参数类型推断出wrapper的类型
我无法通过 infer
实现:
function wrap<T extends ((...args: any[]) => any) & { flag?: string }>(wrapped: T extends infer R ? R : never): T {
let wrapper = function () {
// ...
return wrapped(...arguments);
} as T;
wrapper.flag = wrapped.flag;
return wrapper;
}
let wrapped = (a: number, b: number) => `${a}{b}`;
// wrapper is ((...args: any[]) => any) & { flag?: string }
// should be ((a: number, b: number) => string & { flag?: string }
let wrapper = wrap(wrapped);
// foo is any
// should be string
let foo = wrapper('invalid');
在这种情况下如何在不明确指定包装器类型的情况下完成此操作?
给定类型 T extends infer R ? R : never
的值,编译器几乎肯定无法推断出对 T
有用的任何内容(因为它倾向于推迟评估这种条件类型,直到为时已晚T
进行推断)。由于该类型本质上等同于 T
,因此我们将在下文中使用 T
。
我认为 wrap()
签名和实现应该是这样的:
function wrap<T extends ((...args: any[]) => any)>(
wrapped: T & { flag?: string }): T & { flag?: string } {
let wrapper = function () {
// ...
return wrapped(...arguments);
} as T & { flag?: string };
wrapper.flag = wrapped.flag;
return wrapper;
}
也就是说,我们将只使用通用参数 T
来表示函数类型,而将 {flag?: string}
作为单独的非通用对象类型保留 T
将相交。您需要这样做的原因与可选属性的可分配性规则有关:
在 TypeScript 中,对象类型 缺少 属性 被认为可分配给具有 可选 [=54] 的对象=] 任何类型。例如,类型 {foo: string}
可分配给类型 {foo: string, bar?: number}
。如果你有一个通用类型 T extends {foo: string, bar?: number}
,T
可能是 {foo: string}
,完全没有 bar
属性。
在你的例子中,如果你有一个通用类型 T extends ((...args: any[])=>any) & {flag?: string}
,并传入一个没有已知 flag
属性 的函数作为 T
的推理站点,编译器将推断 T
只是没有 flag
属性 的函数类型。然后函数输出也没有flag
属性,郁闷
为了确保编译器将函数的输入和输出解释为具有可选的 flag
属性,我们需要在输入和输出中显式包含它,并且不要让编译器有机会忘记它。
好的;希望有所帮助;祝你好运!