TypeScript-使用正确版本的setTimeout(节点与窗口)


111

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

setTimeout(handler: (...args: any[]) => void, timeout: number): number;

但是,编译器将其解析为节点实现,它将返回NodeJS.Timer:

setTimeout(callback: (...args: any[]) => void, ms: number, ...args: any[]): NodeJS.Timer;

该代码未在节点中运行,但是节点类型作为对其他内容(不知道是什么)的依赖而被引入。

如何指示编译器选择所需的版本setTimeout

这是有问题的代码:

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

这会产生编译器错误:

TS2322:不能将“计时器”类型分配给“数字”类型。


您的tsconfig.json中是否有type:[“ node”]?参见stackoverflow.com/questions/42940954/…–
koe

@koe不,我在tsconfig文件中没有类型:[“ node”]选项。但是节点类型作为npm对其他东西的依赖而被引入。
凯文·提格

1
您还可以在tsconfig.json中显式定义“类型”-省略“节点”时,不会在编译中使用它。例如“类型”:[“ jQuery”]
koe

1
令人惊讶的是,@ koe的答案(使用“类型”选项)没有任何投票,它是唯一真正的正确答案。
Egor Nepomnyaschih

Answers:


87

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

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

而且,应该可以window显式地使用对象,而无需any

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

25
我认为另一个(window.setTimeout)应该是这个问题的正确答案,因为它是最清晰的解决方案。
amik

5
如果您使用的是any类型,那么您实际上并没有给出TypeScript答案。
S..19年

同样,该number类型将导致TypeScript特定的lint错误,因为该setTimeout函数需要的不止于此。
秒。

1
window.setTimeout可能会导致单元测试框架(node.js)出现问题。最好的解决方案是使用let n: NodeJS.Timeoutn = setTimeout
cchamberlain

102
let timer: ReturnType<typeof setTimeout> = setTimeout(() => { ... });

clearTimeout(timer);

通过使用,ReturnType<fn>您将获得与平台的独立性。你不会被强制使用any,也没有window.setTimeout哪个,如果你运行的代码没有服务器的NodeJS(如服务器端渲染页面)打破。


好消息,这也与Deno兼容!


9
我的理解是,这是正确的答案,应该被接受,因为它为每个支持setTimeout/ clearTimeout且不使用的平台提供了正确的类型定义any
afenster

11
如果您要编写同时在NodeJS和浏览器上运行的库,这就是解决方案。
yqlim

返回类型是NodeJS.Timeout,如果使用setTimeout直接number,如果使用window.setTimeout。不需要使用ReturnType
cchamberlain

@cchamberlain在运行setTimeout函数时需要它,并且期望将其结果存储在变量中。在TS游乐场自己尝试。
Akxe

14

我想这取决于您将在哪里运行代码。

如果您的运行时目标是服务器端Node JS,请使用:

let timeout: NodeJS.Timeout;
global.clearTimeout(timeout);

如果您的运行时目标是浏览器,请使用:

let timeout: number;
window.clearTimeout(timeout);

4

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

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

它可以与setTimeout / setInterval / clearTimeout / clearInterval方法的两种实现一起使用。


2
是的,确实有效。我还意识到,我可以直接在window对象上指定方法:window.setTimeout(...)。不确定这是否是最好的方法,但我会暂时坚持下去。
凯文·提格

1
您可以在打字稿中正确导入NodeJS命名空间,请参见此答案
hlovdal

要实际回答问题(“如何指示编译器选择所需的版本”),可以改用window.setTimeout(),如下所示@dhilt回答。
安森·范多伦

window.setTimeout可能不适用于单元测试框架。有一种可以在这里使用的类型NodeJS.Timeout。您可能会认为您不在节点环境中,但我有个好消息:Webpack / TypeScript等正在执行node.js。
cchamberlain

4

这可能适用于旧版本,但是对于TypeScript版本^3.5.3和Node.js版本^10.15.3,您应该能够从Timers模块中导入特定于Node的功能,即:

import { setTimeout } from 'timers';

这将返回您可以传递给的Timeout类型的实例:NodeJS.TimeoutclearTimeout

import { clearTimeout, setTimeout } from 'timers';

const timeout: NodeJS.Timeout = setTimeout(function () { /* snip */  }, 500);

clearTimeout(timeout);

1
同样,如果您希望使用的浏览器版本setTimeout,则类似之类的东西 const { setTimeout } = window将清除这些错误。
杰克·蒸汽

1
  • 如果您想找到有关计时器的打字稿的真正解决方案,请执行以下操作:

    错误是返回类型“数字”,它不是计时器或其他任何东西。

    这适用于打字稿版本〜> 2.7解决方案:

export type Tick = null | number | NodeJS.Timer;

现在我们修复了所有问题,只需这样声明:

 import { Tick } from "../../globals/types";

 export enum TIMER {
    INTERVAL = "INTERVAL",
    TIMEOUT = "TIMEOUT", 
 };

 interface TimerStateI {
   timeInterval: number;
 }

 interface TimerI: TimerStateI {
   type: string;
   autoStart: boolean;
   isStarted () : bool;
 }

     class myTimer extends React.Component<TimerI, TimerStateI > {

          private myTimer: Tick = null;
          private myType: string = TIMER.INTERVAL;
          private started: boll = false;

          constructor(args){
             super(args);
             this.setState({timeInterval: args.timeInterval});

             if (args.autoStart === true){
               this.startTimer();
             }
          }

          private myTick = () => {
            console.log("Tick");
          }    

          private startTimer = () => {

            if (this.myType === TIMER.INTERVAL) {
              this.myTimer = setInterval(this.myTick, this.timeInterval);
              this.started = true;
            } else if (this.myType === TIMER.TIMEOUT) {
              this.myTimer = setTimeout(this.myTick, this.timeInterval);
              this.started = true;
            }

          }

         private isStarted () : bool {
           return this.started;
         }

     ...
     }
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.