You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React Hook类型错误:ReturnType<typeof setTimeout>与setTimeout返回类型不匹配

React Hook类型校验错误:setTimeout返回类型不兼容

问题场景

创建自定义React Hook useTimeoutEffect 时,遇到TypeScript类型校验失败,错误提示Type 'number' is not assignable to type 'Timeout',出现在给useRef赋值setTimeout返回值的行。

Hook代码

import { useEffect, useRef } from "react";

export function useTimeoutEffect<TArgs extends any[]>(
  callback: (...args: TArgs) => void,
  ms?: number,
  ...args: TArgs
): void {
  const timeoutRef = useRef<ReturnType<typeof setTimeout>>();
  useEffect(() => {
    if (timeoutRef.current !== undefined) {
      clearTimeout(timeoutRef.current);
    }
    // 类型错误行
    timeoutRef.current = setTimeout(callback, ms, args);
    return () => {
      clearTimeout(timeoutRef.current);
      timeoutRef.current = undefined;
    };
  }, [ms, args]);
}

tsconfig.json配置

{
  "compilerOptions": {
    "target": "esnext",
    "lib": [
      "dom",
      "dom.iterable",
      "esnext"
    ],
    "allowJs": false,
    "skipLibCheck": true,
    "declarationMap": true,
    "esModuleInterop": true,
    "allowSyntheticDefaultImports": true,
    "strict": true,
    "declaration": true,
    "outDir": "./dist",
    "forceConsistentCasingInFileNames": true,
    "noFallthroughCasesInSwitch": true,
    "module": "esnext",
    "moduleResolution": "node",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "jsx": "react-jsx",
    "noEmit": true
  },
  "include": [
    "src"
  ],
  "exclude": [
    "**/*.test.ts",
    "**/*.test.tsx",
    "src/test-support/*"
  ]
}

错误信息

$ C:\p\spring-docker\node_modules\.bin\tsc
src/useTimeoutEffect/useTimeoutEffect.ts:19:5 - error TS2322: Type 'number' is not assignable to type 'Timeout'.

19     timeoutRef.current = setTimeout(callback, ms, args);
       ~~~~~~~~~~~~~~~~~~

Found 1 error in src/useTimeoutEffect/useTimeoutEffect.ts:19

已尝试的操作

  • 使用ReturnType<typeof setTimeout>定义useRef的类型
  • 未在tsconfig中显式添加"types": ["node"]
  • 尝试添加"types": []清空类型依赖,问题依旧
  • 目前临时通过as any绕过错误,但需要合理解决方案

问题原因

类型冲突来自环境差异:

  • 浏览器环境中,setTimeout返回数字类型的定时器ID
  • Node.js环境中(项目通过jest引入了@types/node),setTimeout返回**Timeout对象**
    TypeScript同时加载了DOM和Node的类型定义,导致ReturnType<typeof setTimeout>的类型歧义,最终校验失败。

解决方案

方案1:明确使用浏览器环境的定时器类型

直接将useRef的类型定义为浏览器环境的number | undefined,同时修复参数传递和依赖数组的问题:

import { useEffect, useRef } from "react";

export function useTimeoutEffect<TArgs extends any[]>(
  callback: (...args: TArgs) => void,
  ms?: number,
  ...args: TArgs
): void {
  // 浏览器环境下定时器ID为number类型
  const timeoutRef = useRef<number | undefined>();
  useEffect(() => {
    if (timeoutRef.current !== undefined) {
      clearTimeout(timeoutRef.current);
    }
    // 修复参数传递:用...args展开,确保回调接收正确的参数
    timeoutRef.current = setTimeout(callback, ms, ...args);
    return () => {
      clearTimeout(timeoutRef.current);
      timeoutRef.current = undefined;
    };
  }, [ms, ...args]); // 依赖数组展开args,避免因数组引用变化导致不必要的effect执行
}

方案2:显式指定环境类型(兼容多环境)

如果需要同时兼容浏览器和Node.js环境,可以通过类型断言明确指定,并保留ReturnType<typeof setTimeout>的定义:

// 保持原useRef类型定义
const timeoutRef = useRef<ReturnType<typeof setTimeout>>();

// 在赋值时添加类型断言
timeoutRef.current = setTimeout(callback, ms, ...args) as ReturnType<typeof setTimeout>;

方案3:限制tsconfig的类型依赖

在tsconfig中明确只加载DOM类型,避免Node类型干扰:

{
  "compilerOptions": {
    // ...其他配置
    "types": ["dom"]
  }
}

注:如果需要在jest测试中使用Node类型,可以创建单独的tsconfig.test.json,添加"types": ["dom", "node"]。


内容的提问来源于stack exchange,提问作者Archimedes Trajano

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.08 15:56:02