如何在 TypeScript 中对两个重载函数使用相同的参数?
How to use the same parameters with two overloaded functions in TypeScript?
我试图在 typescript 的重载函数中调用另一个重载函数。由于类型 Func2
与类型 Func1
相同,因此可以肯定从 func2
传递到 func1
的参数将被正确键入。然而,打字稿似乎无法接受,并抛出一个错误。
type Func1 = {
(a: string, b: string): void
(a: undefined, b: undefined): void
}
const func1: Func1 = (a, b) => {
console.log(a, b)
}
type Func2 = {
(a: string, b: string): void
(a: undefined, b: undefined): void
}
const func2: Func2 = (a, b) => {
func1(a, b)
console.log(a, b)
}
/*
No overload matches this call.
Overload 1 of 2, '(a: string, b: string): void', gave the following error.
Argument of type 'string | undefined' is not assignable to parameter of type 'string'.
Type 'undefined' is not assignable to type 'string'.
Overload 2 of 2, '(a: undefined, b: undefined): void', gave the following error.
Argument of type 'string | undefined' is not assignable to parameter of type 'undefined'.
Type 'string' is not assignable to type 'undefined'.
*/
我该如何解决?
编辑: 这是我尝试的实际实现:
type ParseRandomArgs = {
(a: undefined, b: undefined): [number, number]
(a: number, b: undefined): [number, number]
(a: number, b: number): [number, number]
(a: [number, number], b: undefined): [number, number]
(a: [number], b: undefined): [number, number]
}
type RandomNumber = {
(a: undefined, b: undefined): number
(a: number, b: undefined): number
(a: number, b: number): number
(a: [number, number], b: undefined): number
(a: [number], b: undefined): number
}
const isNullish = (value: any) => value === undefined || value === null
const parseRandomArgs: ParseRandomArgs = (a, b) => {
if (Array.isArray(a)) {
if (a.length === 2) return a
return [0, a[0]]
}
else if (isNullish(b)) return [0, isNullish(a) ? 1 : a as number]
else return [a as number, b as number]
}
const randomFloat: RandomNumber = (a, b) => {
let [min, max] = parseRandomArgs(a, b) // [min, max]
return Math.random() * (max - min) + min
}
const randomInt: RandomNumber = (a, b) => {
let [min, max] = parseRandomArgs(a, b) // [min, max]
min = Math.ceil(min)
max = Math.floor(max)
return Math.floor(Math.random() * (max - min) + min)
}
我认为你尝试制作太干净的代码
考虑第二个例子(随机数):typescript实际上加入了所有可能的参数变体
const randomInt: RandomNumber = (a, b) => {
//a: number | [number, number] | [number] | undefined
//b: number | undefined
}
您的 RandomNumber/ParseRandomArgs 定义只能用于调用验证
所以第一个解决方案是用所有参数的联合扩展 ParseRandomArgs
type ParseRandomArgs = {
...
(a: [number]| [number, number] | number | undefined, b: number | undefined): [number, number]
}
其次,将此类联合移动到受保护的函数并将 ParseRandomArgs 转换为代理
const _parseRandomArgs = function(a: [number] | [number,number] | number | undefined ,b?: number | undefined): [number,number]{
...
}
const parseRandomArgs: ParseRandomArgs = (a, b) => {
return _parseRandomArgs(a,b);
}
const randomFloat: RandomNumber = (a, b) => {
let [min, max] = _parseRandomArgs(a, b) // [min, max]
return Math.random() * (max - min) + min
}
并且我建议您将所有未定义的参数设为可选
完整代码在Playground
正如@Mike 所提到的,使 undefined
参数可选使重载更容易解决。这应该有效:
type RandomGenerator = {
(start: number, end?: number): number
(range: [number, number]): number;
(range: [number]): number;
}
const randomFloat: RandomGenerator = (start, end?) => {
const [min, max] = parseRandomArgs(start, end as number | undefined);
return computeRandom(min, max);
}
const randomInt: RandomGenerator = (start, end?) => {
const [min, max] = parseRandomArgs(start, end as number | undefined);
return Math.floor(computeRandom(min, max));
}
const computeRandom = (min: number, max: number): number => {
return Math.random() * (max - min) + min;
}
const parseRandomArgs = (first: number | [number, number] | [number], second: number | undefined): [number, number] => {
let args: [number, number];
if (Array.isArray(first) && first.length === 1) {
args = [0, first[0]];
} else if (Array.isArray(first) && first.length === 2) {
args = first;
} else {
args = Number.isFinite(second) ? [first, second as number] : [0, first];
}
const [min, max] = args;
return [Math.ceil(min), Math.floor(max)];
}
您不需要复制参数,甚至不需要使用任何断言:您可以使用 type predicate 让编译器区分空类型。
通过为参数使用带标签的元组,您甚至可以通过编辑器中有用的 IntelliSense 建议获得更好的开发人员体验:
type Fn<
Params extends unknown[] = any[],
Result = any,
> = (...args: Params) => Result;
type CommonParams = [
[max?: number],
[min: number, max?: number],
[minAndMax: [min: number, max: number]],
[maxOnly: [max: number]],
];
type ParseFn = Fn<CommonParams[number], [number, number]>;
type RandomFn = Fn<CommonParams[number], number>;
function isNullish <T>(value: T): value is Exclude<T, NonNullable<T>> {
return value === undefined || value === null;
}
const parseRandomArgs: ParseFn = (...args) => {
const [a, b] = args;
if (Array.isArray(a)) return a.length === 2 ? a : [0, a[0]];
if (isNullish(a)) return [0, 1];
return isNullish(b) ? [0, a] : [a, b];
};
const randomFloat: RandomFn = (...args) => {
const [min, max] = parseRandomArgs(...args);
return Math.random() * (max - min) + min;
};
const randomInt: RandomFn = (...args) => {
let [min, max] = parseRandomArgs(...args);
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min) + min);
};
我试图在 typescript 的重载函数中调用另一个重载函数。由于类型 Func2
与类型 Func1
相同,因此可以肯定从 func2
传递到 func1
的参数将被正确键入。然而,打字稿似乎无法接受,并抛出一个错误。
type Func1 = {
(a: string, b: string): void
(a: undefined, b: undefined): void
}
const func1: Func1 = (a, b) => {
console.log(a, b)
}
type Func2 = {
(a: string, b: string): void
(a: undefined, b: undefined): void
}
const func2: Func2 = (a, b) => {
func1(a, b)
console.log(a, b)
}
/*
No overload matches this call.
Overload 1 of 2, '(a: string, b: string): void', gave the following error.
Argument of type 'string | undefined' is not assignable to parameter of type 'string'.
Type 'undefined' is not assignable to type 'string'.
Overload 2 of 2, '(a: undefined, b: undefined): void', gave the following error.
Argument of type 'string | undefined' is not assignable to parameter of type 'undefined'.
Type 'string' is not assignable to type 'undefined'.
*/
我该如何解决?
编辑: 这是我尝试的实际实现:
type ParseRandomArgs = {
(a: undefined, b: undefined): [number, number]
(a: number, b: undefined): [number, number]
(a: number, b: number): [number, number]
(a: [number, number], b: undefined): [number, number]
(a: [number], b: undefined): [number, number]
}
type RandomNumber = {
(a: undefined, b: undefined): number
(a: number, b: undefined): number
(a: number, b: number): number
(a: [number, number], b: undefined): number
(a: [number], b: undefined): number
}
const isNullish = (value: any) => value === undefined || value === null
const parseRandomArgs: ParseRandomArgs = (a, b) => {
if (Array.isArray(a)) {
if (a.length === 2) return a
return [0, a[0]]
}
else if (isNullish(b)) return [0, isNullish(a) ? 1 : a as number]
else return [a as number, b as number]
}
const randomFloat: RandomNumber = (a, b) => {
let [min, max] = parseRandomArgs(a, b) // [min, max]
return Math.random() * (max - min) + min
}
const randomInt: RandomNumber = (a, b) => {
let [min, max] = parseRandomArgs(a, b) // [min, max]
min = Math.ceil(min)
max = Math.floor(max)
return Math.floor(Math.random() * (max - min) + min)
}
我认为你尝试制作太干净的代码
考虑第二个例子(随机数):typescript实际上加入了所有可能的参数变体
const randomInt: RandomNumber = (a, b) => {
//a: number | [number, number] | [number] | undefined
//b: number | undefined
}
您的 RandomNumber/ParseRandomArgs 定义只能用于调用验证
所以第一个解决方案是用所有参数的联合扩展 ParseRandomArgs
type ParseRandomArgs = {
...
(a: [number]| [number, number] | number | undefined, b: number | undefined): [number, number]
}
其次,将此类联合移动到受保护的函数并将 ParseRandomArgs 转换为代理
const _parseRandomArgs = function(a: [number] | [number,number] | number | undefined ,b?: number | undefined): [number,number]{
...
}
const parseRandomArgs: ParseRandomArgs = (a, b) => {
return _parseRandomArgs(a,b);
}
const randomFloat: RandomNumber = (a, b) => {
let [min, max] = _parseRandomArgs(a, b) // [min, max]
return Math.random() * (max - min) + min
}
并且我建议您将所有未定义的参数设为可选
完整代码在Playground
正如@Mike 所提到的,使 undefined
参数可选使重载更容易解决。这应该有效:
type RandomGenerator = {
(start: number, end?: number): number
(range: [number, number]): number;
(range: [number]): number;
}
const randomFloat: RandomGenerator = (start, end?) => {
const [min, max] = parseRandomArgs(start, end as number | undefined);
return computeRandom(min, max);
}
const randomInt: RandomGenerator = (start, end?) => {
const [min, max] = parseRandomArgs(start, end as number | undefined);
return Math.floor(computeRandom(min, max));
}
const computeRandom = (min: number, max: number): number => {
return Math.random() * (max - min) + min;
}
const parseRandomArgs = (first: number | [number, number] | [number], second: number | undefined): [number, number] => {
let args: [number, number];
if (Array.isArray(first) && first.length === 1) {
args = [0, first[0]];
} else if (Array.isArray(first) && first.length === 2) {
args = first;
} else {
args = Number.isFinite(second) ? [first, second as number] : [0, first];
}
const [min, max] = args;
return [Math.ceil(min), Math.floor(max)];
}
您不需要复制参数,甚至不需要使用任何断言:您可以使用 type predicate 让编译器区分空类型。
通过为参数使用带标签的元组,您甚至可以通过编辑器中有用的 IntelliSense 建议获得更好的开发人员体验:
type Fn<
Params extends unknown[] = any[],
Result = any,
> = (...args: Params) => Result;
type CommonParams = [
[max?: number],
[min: number, max?: number],
[minAndMax: [min: number, max: number]],
[maxOnly: [max: number]],
];
type ParseFn = Fn<CommonParams[number], [number, number]>;
type RandomFn = Fn<CommonParams[number], number>;
function isNullish <T>(value: T): value is Exclude<T, NonNullable<T>> {
return value === undefined || value === null;
}
const parseRandomArgs: ParseFn = (...args) => {
const [a, b] = args;
if (Array.isArray(a)) return a.length === 2 ? a : [0, a[0]];
if (isNullish(a)) return [0, 1];
return isNullish(b) ? [0, a] : [a, b];
};
const randomFloat: RandomFn = (...args) => {
const [min, max] = parseRandomArgs(...args);
return Math.random() * (max - min) + min;
};
const randomInt: RandomFn = (...args) => {
let [min, max] = parseRandomArgs(...args);
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min) + min);
};