我正在升级一些旧的TypeScript代码以使用最新的编译器版本,我在调用setTimeout时有麻烦。代码期望调用浏览器的setTimeout函数,该函数返回一个数字:

setTimeout(处理程序:(…Args: any[]) => void, timeout: number): number;

但是,编译器将此解析为节点实现,该实现将返回NodeJS。定时器:

setTimeout(回调:(…Args: any[]) => void, ms: number,…args: any[]): NodeJS.Timer;

这段代码没有在节点中运行,但是节点类型被拉入作为对其他东西的依赖(不确定是什么)。

如何指示编译器选择我想要的setTimeout版本?

下面是问题代码:

let n: number;
n = setTimeout(function () { /* snip */  }, 500);

这将产生编译器错误:TS2322:类型'Timer'不能分配给类型'number'。


当前回答

我在使用React时也遇到了类似的问题,解决方法如下:

import React, { useRef, useState, useEffect} from 'react';
import { Alert } from '../types/alerts';

const AlertComponent: React.FC<{alert: Alert}> = ({alert}) => {
  const intervalRef = useRef<NodeJS.Timeout>();
  const [count, setCount] = useState(alert.timeLimit)

  useEffect(() => {
    intervalRef.current = setInterval(
      () => {setCount((count) => count - 1)},
      1000
    )

    return () => {
      clearInterval(intervalRef.current as NodeJS.Timeout)
    }
  }, [])

  return (
    <p>{count}</p>
  )
}

export default AlertComponent;

在我的useEffect()钩子中,我有clearInterval(intervalRef。因为clearInterval显式地查找NodeJS。Timeout |未定义,所以我必须去掉未定义的部分。

其他回答

TS2322:类型'Timer'不能分配给类型'number'。

简单的解决方案

ABC:任何;

为了阿斯金

abc = setInterval或abc = setTimeout

我在使用React时也遇到了类似的问题,解决方法如下:

import React, { useRef, useState, useEffect} from 'react';
import { Alert } from '../types/alerts';

const AlertComponent: React.FC<{alert: Alert}> = ({alert}) => {
  const intervalRef = useRef<NodeJS.Timeout>();
  const [count, setCount] = useState(alert.timeLimit)

  useEffect(() => {
    intervalRef.current = setInterval(
      () => {setCount((count) => count - 1)},
      1000
    )

    return () => {
      clearInterval(intervalRef.current as NodeJS.Timeout)
    }
  }, [])

  return (
    <p>{count}</p>
  )
}

export default AlertComponent;

在我的useEffect()钩子中,我有clearInterval(intervalRef。因为clearInterval显式地查找NodeJS。Timeout |未定义,所以我必须去掉未定义的部分。

2021年更新

Akxe的答案是Typescript 2.3中引入的ReturnType<Type>技术:

let n: ReturnType<typeof setTimeout>;
n = setTimeout(cb, 500);

这很好,似乎比显式强制转换更受欢迎。但在本例中,“n”的结果类型是“NodeJS”。Timeout”,可以这样使用:

let n: NodeJS.Timeout;
n = setTimeout(cb, 500);

ReturnType/NodeJS的唯一问题。超时方法是,特定于浏览器的环境中的数值操作仍然需要强制转换:

if ((n as unknown as number) % 2 === 0) {
  clearTimeout(n);
}

原来的答案

一个不影响变量声明的解决方法:

let n: number;
n = setTimeout(function () { /* snip */  }, 500) as unknown as number;

此外,在特定于浏览器的环境中,可以使用没有强制转换的窗口对象:

let n: number;
n = window.setTimeout(function () { /* snip */  }, 500);

我通过设置解决了这个问题

tsconfig.json:

{
  "compilerOptions": {
    "skipLibCheck": true,
  }
}

然后创建。d.ts

* .d.ts:

declare namespace NodeJS {
    type Timeout = number;
    type Timer = number;
}

Typescript版本4.2.3

我遇到了同样的问题,我们的团队决定使用的解决方法是使用“any”作为计时器类型。例如:

let n: any;
n = setTimeout(function () { /* snip */  }, 500);

它将与setTimeout/setInterval/clearTimeout/clearInterval方法的两种实现一起工作。