Is it possible to declare a typescript function which works on both numbers and bigints?

a 夏天 提交于 2021-02-16 08:43:52

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!