Inf*_*ght 11 oop overloading typescript
关于TypeScript 中函数重载的工作原理有很多问题(例如,TypeScript 函数重载)。但是没有像“为什么它以这种方式工作?”这样的问题。现在函数重载看起来像这样:
function foo(param1: number): void;
function foo(param1: number, param2: string): void;
function foo(...args: any[]): void {
if (args.length === 1 && typeof args[0] === 'number') {
// implementation 1
} else if (args.length === 2 && typeof args[0] === 'number' && typeof args[1] === 'string') {
// implementation 2
} else {
// error: unknown signature
}
}
Run Code Online (Sandbox Code Playgroud)
我的意思是,Typescript 的创建是为了通过添加一些所谓的“语法糖”来使程序员的生活更轻松,从而提供 OOD 的优势。那么为什么 Typescript 不能代替程序员来做这些烦人的事情呢?例如,它可能看起来像:
function foo(param1: number): void {
// implementation 1
};
function foo(param1: number, param2: string): void {
// implementation 2
};
foo(someNumber); // result 1
foo(someNumber, someString); // result 2
foo(someNumber, someNumber); // ts compiler error
Run Code Online (Sandbox Code Playgroud)
这个 Typescript 代码将被转换为以下 Javascript 代码:
function foo_1(param1) {
// implementation 1
};
function foo_2(param1, param2) {
// implementation 2
};
function foo(args) {
if (args.length === 1 && typeof args[0] === 'number') {
foo_1(args);
} else if (args.length === 2 && typeof args[0] === 'number' && typeof args[1] === 'string') {
foo_2(args);
} else {
throw new Error('Invalid signature');
}
};
Run Code Online (Sandbox Code Playgroud)
而且我没有找到任何原因,为什么 Typescript 不能像这样工作。有任何想法吗?
如果您愿意,考虑如何在 TypeScript 中实现“真正的”函数重载是一个有趣的练习。让编译器采用一堆单独的函数并从中生成一个函数是很容易的。但是在运行时,这个单个函数必须根据参数的数量和类型知道要调用几个底层函数中的哪一个。参数的数量肯定可以在运行时确定,但是参数的类型被完全擦除,所以没有办法实现,你就卡住了。
当然,您可能会违反 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;
}
Run Code Online (Sandbox Code Playgroud)
该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
Run Code Online (Sandbox Code Playgroud)
有用。好极了?
总结一下:在运行时没有某种方法来确定参数的类型是不可能的,这在一般情况下需要开发人员指定的类型保护。因此,您可以通过要求开发人员为每个重载提供类型保护来进行重载,或者通过使用单个实现和多个调用签名来做他们现在所做的事情。后者更简单。
希望能提供一些见解。祝你好运!
| 归档时间: |
|
| 查看次数: |
3444 次 |
| 最近记录: |