【问题标题】:Is it possible to declare a typescript function which works on both numbers and bigints?是否可以声明一个适用于数字和大整数的打字稿函数?
【发布时间】:2021-03-24 14:33:31
【问题描述】:

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

const sumOfSquares = (a,b) => a*a + b*b;
sumOfSquares(3, 4); // returns 25
sumOfSquares(3n, 4n); // returns 25n
sumOfSquares(3n, 4); // throws a TypeError

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

const sumOfSquares = <N extends bigint | number>(a: N, b: N): N =>
  a * a + b * b;

但是编译器拒绝了这个:

语义错误 TS2322:类型“数字”不可分配给类型“N”。
“number”可以分配给“N”类型的约束,但“N”可以用约束“number |”的不同子类型来实例化。 bigint'。

是否有不同的方式来编写类型声明以使其正常工作?

【问题讨论】:

  • 你总是可以尝试重载,虽然它不像泛型那样干净。
  • 我认为你甚至不能混合 bigint 和 number,例如1n+2无效,那么混合类型有什么意义呢?
  • The overload I thought of 不起作用。看起来 TypeScript 对 bigints 的支持参差不齐 - 例如它仍然假定* 总是返回一个数字。
  • @ABOS 目的是您需要传递两个数字或两个 bigint。但你是对的,这就是编译器的假设:你可以用联合 number | bigint(而不是其中一个)实例化 N 并破坏函数。
  • @Bergi,我在下面添加了重载版本,它很冗长,但似乎工作

标签: javascript typescript types bigint


【解决方案1】:

超载有用吗?

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】:

    解决办法如下:

    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
    

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

    
    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
    

    如果您将第一个参数作为简单数字传递,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
    

    【讨论】:

    • 使用人们在我的回答中抱怨的测试用例,你在这篇文章中的第二个回答没有通过。 typescriptlang.org/play?target=99#code/…
    • 任何类型系统都无法判断出是RANDOM产物的类型
    • 这正是我的观点,请参阅我的回答中的 cmets
    • 我认为你只是不想在你的Overloading 界面中出现第三个调用签名
    • @jcalz 我试过了。没有第三次重载,TS 抱怨函数定义
    猜你喜欢
    • 2021-05-23
    • 1970-01-01
    • 2016-03-27
    • 2012-11-08
    • 2016-04-04
    • 2020-05-02
    • 2013-01-22
    • 2019-12-14
    • 1970-01-01
    相关资源
    最近更新 更多