问题
In plain untyped Javascript, it's not so hard to write a function which can operate on either numbers or bigints, depending on the arguments which are passed in:
const sumOfSquares = (a,b) => a*a + b*b;
sumOfSquares(3, 4); // returns 25
sumOfSquares(3n, 4n); // returns 25n
sumOfSquares(3n, 4); // throws a TypeError
It seems like there ought to be a way to declare this function in typescript so that the compiler will enforce that the arguments will work together. I tried
const sumOfSquares = <N extends bigint | number>(a: N, b: N): N =>
a * a + b * b;
But the compiler rejects this:
semantic error TS2322: Type 'number' is not assignable to type 'N'.
'number' is assignable to the constraint of type 'N', but 'N' could be instantiated with a different subtype of constraint 'number | bigint'.
Is there a different way to write the type declaration so that it will work?
回答1:
Does overload work?
function isBigInt(a: bigint | number): a is bigint {
return typeof (a) === 'bigint'
}
function isNumber(a: bigint | number): a is number {
return typeof (a) === 'number'
}
function doSomething(a: bigint, b: bigint): bigint;
function doSomething(a: number, b: number): number;
function doSomething(a: bigint | number, b: bigint | number): bigint | number | never {
if (isBigInt(a) && isBigInt(b))
return a + b;
else if (isNumber(a) && isNumber(b))
return a + b;
throw 'error';
}
let a = doSomething(1, 1)
let b = doSomething(1n, 1n)
let c = doSomething(1n, 1)
回答2:
Here is the solution:
function sumOfSquares<N extends number>(a: N, b: N):N
function sumOfSquares<N extends bigint>(a: N, b: N):N
function sumOfSquares<N extends bigint | number>(a: N, b: N) {
return a * a + b * b;
}
sumOfSquares(2n,2n) // ok
sumOfSquares(2,2) // ok
sumOfSquares(2n,2) // error
sumOfSquares(2,2n) // error
Btw, you can also define overloadings for arrow function:
interface Overloading {
<N extends number>(a: N, b: N): N
<N extends bigint>(a: N, b: N): N
<N extends bigint | number>(a: N, b: N): N
}
const sumOfSquares: Overloading = <N extends bigint | number>(a: N, b: N) => a * a + b * b;
sumOfSquares(2n, 2n) // ok
sumOfSquares(2, 2) // ok
sumOfSquares(2n, 2) // error
sumOfSquares(2, 2n) // error
If you passed first argument as simple number, TS will expect second argument to have the same type. Same behaviour with BigInt's
UPDATE
I should have beed add one extra generic to make it work.
Thanks @jcalz for pointing me in a right direction:
function sumOfSquares<A extends number, B extends number>(a: A, b: B): number
function sumOfSquares<A extends bigint, B extends bigint>(a: A, b: B): bigint
function sumOfSquares<A extends number | bigint, B extends A>(a: A, b: B): bigint | number {
return a * a + b * b
};
const x = 3n;
let y: number | bigint;
if (Math.random() < 0.5) y = 4;
else y = 4n;
const result = sumOfSquares(x, y) // There should be an error here
const result2 = sumOfSquares(3n, 4) // There should be an error here too
const result3 = sumOfSquares(3, 4n) // There should be an error here too
const result4 = sumOfSquares(3, 4) // ok
const result5 = sumOfSquares(3n, 4n) // ok
来源:https://stackoverflow.com/questions/65280785/is-it-possible-to-declare-a-typescript-function-which-works-on-both-numbers-and