Typescript NodeJS.Timer 类型报错解决方案

367 阅读1分钟

最近在写一个基于 Vite + Vitest + React + Typescript的放置类游戏写到计时器的时候遇到一个类型检查的问题

简写代码如下

interface IFisherTimerAction {
  (): void;
}

interface IFisherTimer {
  /**
   * 计时器要执行的action
   *
   * @type {IFisherTimerAction}
   * @memberof IFisherTimer
   */
  action: IFisherTimerAction;
  /**
   * 是否立即执行一次
   * 默认开启
   * @type {boolean}
   * @memberof IFisherTimer
   */
  fireImmediately?: boolean;
}
class Timer {
  public timerId?: number;
  public action: IFisherTimerAction;
  public actionInterval?: number;
  public fireImmediately: boolean;

  constructor({ action, fireImmediately }: IFisherTimer) {
    this.action = action;
    this.fireImmediately = fireImmediately ?? true;
  }

  /**
   * 开始执行计时器任务
   *
   * @param {number} actionInterval
   * @memberof Timer
   */
  public startTimer = (actionInterval: number) => {
    this.actionInterval = actionInterval;
    this.timerId && clearInterval(this.timerId);
    this.timerId = setInterval(() => this.action(), actionInterval);
    this.fireImmediately && this.action();
  };
}

会报错:不能将类型 NodeJS.Timer 分配给类型 number

原因是因为项目使用了 Vitest 作为单元测试框架,会引入 NodeJS 的类型定义文件污染了本应该是浏览器端项目的全局类型

image.png

解决方案

使用 TypescriptReturnType 封装一个 Timer 类型之后便不会报错。原理很简单,使用 ReturnType 来推断计时器 api 的类型

type Timer = ReturnType<typeof setTimeout>;

class Timer {
    public timerId?: Timer;
    ...
    
    public startTimer = (actionInterval: number) => {
        ...
        this.timerId && clearInterval(this.timerId);
        this.timerId = setInterval(() => this.action(), actionInterval);
        ...
      };
}