Это интересное упражнение, чтобы подумать о том, как бы вы реализовали «настоящие» перегрузки функций в TypeScript, если бы захотели.Достаточно просто заставить компилятор взять кучу отдельных функций и сделать из них одну функцию.Но во время выполнения эта единственная функция должна знать, какую из нескольких базовых функций вызывать, основываясь на количестве и типах аргументов.Количество аргументов определенно может быть определено во время выполнения, но типы аргументов полностью стерты , поэтому реализовать это невозможно, и вы застряли.100
Конечно, вы можете нарушить одну из целей разработки TypeScript (в частности, не цель # 5 о добавлении информации о типе среды выполнения), но этого не произойдет.Может показаться очевидным, что, когда вы проверяете number
, вы можете вывести typeof xxx === 'number'
, но что вы будете выводить при проверке пользовательского interface
?Один из способов справиться с этим - попросить разработчика предоставить для каждой перегрузки функции определяемый пользователем тип защиты , который определяет, являются ли аргументы правильными типами.Но теперь дело в том, чтобы разработчики определяли пары вещей для каждой перегрузки функции, что сложнее, чем текущая концепция перегрузки TypeScript.
Для интереса, давайте посмотрим, насколько близко вы можете к этому подойти.как библиотека, которая ожидает функции-и-тип-охранники для создания перегруженной функции.Примерно так (в предположении TS 3.1 или выше):
interface FunctionAndGuard<A extends any[]=any[], R=any, A2 extends any[]= A> {
function: (...args: A) => R,
argumentsGuard: (args: any[]) => args is A2
};
type AsAcceptableFunctionsAndGuards<F extends FunctionAndGuard[]> = { [K in keyof F]:
F[K] extends FunctionAndGuard<infer A, infer R, infer A2> ?
FunctionAndGuard<A2, R, A> : never
}
type UnionToIntersection<U> =
(U extends any ? (k: U) => void : never) extends ((k: infer I) => void) ? I : never
type Lookup<T, K> = K extends keyof T ? T[K] : never;
type FunctionAndGuardsToOverload<F extends FunctionAndGuard[]> =
Lookup<UnionToIntersection<F[number]>, 'function'>;
function makeOverloads<F extends FunctionAndGuard[]>(
...functionsAndGuards: F & AsAcceptableFunctionsAndGuards<F>
): FunctionAndGuardsToOverload<F> {
return ((...args: any[]) =>
functionsAndGuards.find(fg => fg.argumentsGuard(args))!.function(...args)) as any;
}
Функция makeOverloads()
принимает переменное число аргументов FunctionAndGuard
и возвращает одну перегруженную функцию.И попробуйте:
function foo_1(param1: number): void {
// implementation 1
};
function foo_2(param1: number, param2: string): void {
// implementation 2
};
const foo = makeOverloads({
function: foo_1,
argumentsGuard: (args: any[]): args is [number] =>
args.length === 1 && typeof args[0] === 'number'
}, {
function: foo_2,
argumentsGuard: (args: any[]): args is [number, string] =>
args.length === 2 && typeof args[0] === 'number' && typeof args[1] === 'string'
}
);
foo(1); // okay
foo(1, "two"); // okay
foo(1, 2); // error
Работает.Ура?
Напомним: во время выполнения невозможно без какого-либо способа определить типы аргументов, что требует защиты, определенной разработчиком, в общем случае.Таким образом, вы могли бы либо выполнить перегрузку, попросив разработчиков предоставить защиту типов для каждой перегрузки, либо сделать то, что они делают сейчас, имея одну реализацию и несколько сигнатур вызовов.Последнее проще.
Надежда, которая дает некоторое понимание.Удачи!