Типизация декоратора или заместителя в TypeScript
Нужно взять существующую функцию и сделать вокруг неё обёртку, некоего заместителя, прокси, который будет что-то делать перед вызовом функции, но возвращать её результат как есть и передавать ей все аргументы, как есть. Проблема только в том, чтобы декоратор получился точно такого же типа, как исходная функция, у которой есть множество перегрузок.
Код:
function foo(a: number, b: string): number;
function foo(a: string, b: number): boolean;
function foo(a, b): any {
if (typeof a === 'number') {
return 0;
}
return !!b;
}
type FooType = typeof foo;
/*
type FooType = {
(a: number, b: string): number;
(a: string, b: number): boolean;
}
*/
const fooProxy = (...params: Parameters<typeof foo>) => {
// Здесь я хочу, например, залоггировать параметры вызова
console.log('Calling foo with params:', params);
return foo(...params);
}
/*
const fooProxy: (a: string, b: number) => boolean
*/
const fooProxyTyped: FooType = fooProxy;
/*
Ошибка!
Type '(a: string, b: number) => boolean' is not assignable to type '{ (a: number, b: string): number; (a: string, b: number): boolean; }'.
Types of parameters 'a' and 'a' are incompatible.
Type 'number' is not assignable to type 'string'.
*/
Вопрос
Как мне объявить fooProxy так, чтобы её typeof fooProxy был идентичен FooType, не повторяя при этом декларации всех перегрузок foo для fooProxy?
Ответы (1 шт):
Автор решения: qwabra
→ Ссылка
без комментариев. обрати внимание на // (parameter) params:
q: {
type foo =
& ((a: number, b: string) => number)
& ((a: string, b: number) => boolean)
/** important any end type*/
& ((a: any, b: any) => any)
// ;;
const foo: foo = null as any
// ;;
{ let res = foo(1, ''), ch: number = res }
{ let res = foo('', 1), ch: boolean = res }
{ let res = foo('', ''), ch: any = res }
// ;;
qq: {
type FooType = typeof foo;
qqq: {
const fooProxy = (...params: Parameters<FooType>): ReturnType<FooType> => foo(...params)
// const fooProxy: (a: any, b: any) => any
}
// (parameter) params: [any, any]
const fooProxy: FooType = (...params: Parameters<FooType>): ReturnType<FooType> => foo(...params)
// const fooProxy: foo
const fooProxyTyped: FooType = fooProxy;
// const fooProxyTyped: foo
}
}
q: {
/** important any start interface*/
interface foo { (a: any, b: any): any }
interface foo { (a: number, b: string): number }
interface foo { (a: string, b: number): boolean }
// ;;
const foo: foo = null as any
// ;;
{ let res = foo(1, ''), ch: number = res }
{ let res = foo('', 1), ch: boolean = res }
{ let res = foo('', ''), ch: any = res }
// ;;
qq: {
type FooType = typeof foo;
qqq: {
// (parameter) params: [any, any]
const fooProxy = (...params: Parameters<FooType>): ReturnType<FooType> => foo(...params)
// const fooProxy: (a: string, b: number) => boolean
}
qqq: {
// (parameter) params: [string, number]
const fooProxy: FooType = (...params: Parameters<FooType>): ReturnType<FooType> => foo(...params)
// const fooProxy: foo
}
const fooProxy = ((...params: Parameters<FooType>): ReturnType<FooType> => foo(...params)) as FooType
// const fooProxy: foo
const fooProxyTyped: FooType = fooProxy;
// const fooProxyTyped: foo
}
}
q: {
function foo(a: number, b: string): number;
function foo(a: string, b: number): boolean;
/** important */
function foo(a: any, b: any): any;
function foo(a: any, b: any) { return null as any }
// ;;
{ let res = foo(1, ''), ch: number = res }
{ let res = foo('', 1), ch: boolean = res }
{ let res = foo('', ''), ch: any = res }
// ;;
qq: {
type FooType = typeof foo;
qqq: {
const fooProxy = (...params: Parameters<FooType>): ReturnType<FooType> => foo(...params)
// const fooProxy: (a: any, b: any) => any
}
const fooProxy = ((...params: Parameters<FooType>): ReturnType<FooType> => foo(...params)) as FooType
// const fooProxy: {
// (a: number, b: string): number;
// (a: string, b: number): boolean;
// (a: any, b: any): any;
// }
const fooProxyTyped: FooType = fooProxy;
// const fooProxyTyped: {
// (a: number, b: string): number;
// (a: string, b: number): boolean;
// (a: any, b: any): any;
// }
}
}
Для невнимательных
q: {
type foo =
& ((a: number, b: string) => number)
& ((a: string, b: number) => boolean)
& ((a: any, b: any) => any)
// ;;
const foo: foo = null as any
const foo2: foo = foo
// ;;
{ let res = foo2(1, ''), ch: number = res }
{ let res = foo2('', 1), ch: boolean = res }
{ let res = foo2('', ''), ch: any = res }
}
Для тех, кому нужно расширить обёртку
q: {
type foo =
& ((a: number, b: string) => number)
& ((a: string, b: number) => boolean)
& ((a: any, b: any) => any)
// ;;
const foo: foo = null as any
const foo2: foo = foo
// ;;
{ let res = foo2(1, ''), ch: number = res }
{ let res = foo2('', 1), ch: boolean = res }
{ let res = foo2('', ''), ch: any = res }
}
q: {
interface foo1 { (a: any, b: any): any }
interface foo2 { (a: number, b: string): number }
interface foo3 { (a: string, b: number): boolean }
/**import foo1 last */
interface foo extends foo2, foo3, foo1 { }
// ;;
const foo: foo = null as any
const foo2: foo = foo
// ;;
{ let res = foo2(1, ''), ch: number = res }
{ let res = foo2('', 1), ch: boolean = res }
{ let res = foo2('', ''), ch: any = res }
// ;;
qq: {
qqq: {
type fooParams = Parameters<foo>
// type fooParams = [string, number]
type fooeturnType = ReturnType<foo>
// type fooeturnType = boolean
}
qqq: { // РАСШИРЯЕМ ТУТ
type extendParams = [{}, any[]]
type extendReturnType = 'E_X_T_E_N_D'
/**type fooParams = [any, any] | [number, string] | [string, number] */
type fooParams = Parameters<foo1> | Parameters<foo2> | Parameters<foo3> | extendParams
/**type fooReturnType = any */
// type fooReturnType = ReturnType<foo1> | ReturnType<foo2> | ReturnType<foo3>
type fooReturnType<T extends fooParams> =
T extends Parameters<foo2>
? ReturnType<foo2>
: T extends Parameters<foo3>
? ReturnType<foo3>
: T extends extendParams
? extendReturnType
/** important */
: T extends Parameters<foo1>
? ReturnType<foo1>
: never
const foo2: <T extends fooParams>(...params: T & fooParams) => fooReturnType<T> = null as any;
// ;;
{ let res = foo2(1, ''), ch: number = res }
{ let res = foo2('', 1), ch: boolean = res }
{ let res = foo2('', ''), ch: any = res }
{ let res = foo2({}, []), ch: extendReturnType = res/**let res: "E_X_T_E_N_D" */ }
}
}
}