是否可以声明一个同时适用于数字和 bigint 的打字稿函数?

mlc*_*mlc 5 javascript types bigint typescript

在普通的无类型 JavaScript 中,编写一个可以对数字或 bigint 进行操作的函数并不难,具体取决于传入的参数:

\n
const sumOfSquares = (a,b) => a*a + b*b;\nsumOfSquares(3, 4); // returns 25\nsumOfSquares(3n, 4n); // returns 25n\nsumOfSquares(3n, 4); // throws a TypeError\n
Run Code Online (Sandbox Code Playgroud)\n

似乎应该有一种方法在打字稿中声明这个函数,以便编译器强制参数一起工作。我试过

\n
const sumOfSquares = <N extends bigint | number>(a: N, b: N): N =>\n  a * a + b * b;\n
Run Code Online (Sandbox Code Playgroud)\n

但编译器拒绝了这一点:

\n
\n

语义错误 TS2322:类型“number”无法分配给类型“N”。
\n\xc2\xa0 \'number\' 可分配给类型 \'N\' 的约束,但 \'N\' 可以使用约束 \'number | 的不同子类型来实例化。bigint\'。

\n
\n

是否有不同的方法来编写类型声明以使其起作用?

\n

cap*_*ian 0

这是解决方案:

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
Run Code Online (Sandbox Code Playgroud)

顺便说一句,您还可以定义箭头函数的重载:


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
Run Code Online (Sandbox Code Playgroud)

如果您将第一个参数作为简单数字传递,TS 将期望第二个参数具有相同的类型。与 BigInt 的行为相同

更新

我应该添加一个额外的泛型才能使其工作。

感谢@jcalz 为我指明了正确的方向:

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
Run Code Online (Sandbox Code Playgroud)