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

Kev*_*ghe 61 typescript

我正在努力升级一些旧的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);
Run Code Online (Sandbox Code Playgroud)

这会产生编译器错误:

TS2322:类型'定时器'不能分配给'数字'类型.

dhi*_*ilt 56

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

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

此外,应该可以window明确使用该对象,而无需any:

let n: number;
n = window.setTimeout(function () { /* snip */  }, 500);
Run Code Online (Sandbox Code Playgroud)

  • 我认为另一个(`window.setTimeout`)应该是这个问题的正确答案,因为它是最清晰的解决方案. (12认同)
  • 如果您使用的是any类型,那么您实际上并没有给出TypeScript答案。 (4认同)
  • `window.setTimeout` 可能会导致单元测试框架 (node.js) 出现问题。最好的解决方案是使用“let n: NodeJS.Timeout”和“n = setTimeout”。 (4认同)

Akx*_*kxe 25

我建议使用,x: ReturnType<typeof setTimeout>;因为它实际上将独立于所使用的平台工作。

  • 如果直接使用`setTimeout`,返回类型是`NodeJS.Timeout`;如果使用`window.setTimeout`,返回类型是`number`。不应该需要使用 `ReturnType`。 (6认同)
  • 我的理解是,这是正确的答案,应该被接受,因为它为每个支持`setTimeout` /`clearTimeout`的平台提供了正确的类型定义,并且不使用“ any”。 (2认同)
  • 如果您要编写同时在NodeJS和浏览器上运行的库,这就是解决方案。 (2认同)
  • @cchamberlain 当您运行“setTimeout”函数并期望其结果存储在变量中时,您需要它。自己去 TS 游乐场尝试一下吧。 (2认同)
  • 这个解决方案对我来说是正确的。如果不使用它,我的节点应用程序可以使用 TS 正确编译,但是当使用 Jest 单元测试时,它会选择不正确的 window.setTimeout 定义 (2认同)

cwo*_*ter 24

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

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

let timeout: NodeJS.Timeout;
global.clearTimeout(timeout);
Run Code Online (Sandbox Code Playgroud)

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

let timeout: number;
window.clearTimeout(timeout);
Run Code Online (Sandbox Code Playgroud)


Ded*_*Dev 21

这对我来说非常有效。

type Timer = ReturnType<typeof setTimeout>

const timer: Timer = setTimeout(() => {}, 1000)
Run Code Online (Sandbox Code Playgroud)


小智 13

这可能适用于旧版本,但使用 TypeScript 版本^3.5.3和 Node.js 版本^10.15.3,您应该能够从Timers模块导入特定于节点的函数,即:

import { setTimeout } from 'timers';
Run Code Online (Sandbox Code Playgroud)

这将返回一个Timeout类型的实例NodeJS.Timeout,您可以将其传递给clearTimeout

import { clearTimeout, setTimeout } from 'timers';

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

clearTimeout(timeout);
Run Code Online (Sandbox Code Playgroud)

  • 同样,如果您想要浏览器版本的“setTimeout”,类似“const { setTimeout } = window”的内容将清除这些错误。 (4认同)

M M*_*ler 8

还想提一下,规范NodeJS.Timeout包括[Symbol.toPrimitive](): number

interface Timeout extends Timer {
    /**
     * If true, the `Timeout` object will keep the Node.js event loop active.
     * @since v11.0.0
     */
    hasRef(): boolean;
    /**
     * Sets the timer's start time to the current time, and reschedules the timer to
     * call its callback at the previously specified duration adjusted to the current
     * time. This is useful for refreshing a timer without allocating a new
     * JavaScript object.
     *
     * Using this on a timer that has already called its callback will reactivate the
     * timer.
     * @since v10.2.0
     * @return a reference to `timeout`
     */
    refresh(): this;
    [Symbol.toPrimitive](): number;
}
Run Code Online (Sandbox Code Playgroud)

为了兼容性,Node 中的其他超时 API 可以很好地处理普通整数 ID,它们不需要接受对象。这些对象在“服务器”端使用,以允许对保持进程活动和垃圾收集内容进行更精细的控制。例如:

function clearTimeout(timeoutId: NodeJS.Timeout | string | number | undefined): void;
Run Code Online (Sandbox Code Playgroud)

setTimeout这意味着您可以对and的结果使用原始转换setInterval

let timeoutId: number | undefined;
timeoutId = Number(setTimeout(callback, ms));

function clear() {
  clearTimeout(timeoutId);
}
Run Code Online (Sandbox Code Playgroud)

不会与任何一个 API 冲突,同时如果您需要依赖它作为其他 API 合约的原始值,也不会在以后遇到类型问题。


Mar*_*rev 5

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

let n: any;
n = setTimeout(function () { /* snip */  }, 500);
Run Code Online (Sandbox Code Playgroud)

它将与setTimeout/setInterval/clearTimeout/clearInterval方法的两种实现一起使用.

  • 是的,这确实有效.我也意识到我可以直接在window对象上指定方法:window.setTimeout(...).不确定这是否是最好的方式,但我现在还会坚持下去. (2认同)