我正在升级一些旧的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'。
我正在使用RTL测试我的Counter应用程序,特别是在测试一个元素,如果计数达到15就要删除。由于组件在运行测试后被销毁,setTimeout仍然会在此之后运行,并抛出一个错误,说React不能对卸载的组件执行状态更新。因此,基于dhilt的回答,我能够以这种方式修复我的useEffect清理函数:
const [count, setCount] = useState(initialCount);
const [bigSize, setBigSize] = useState(initialCount >= 15);
useEffect(() => {
let id: NodeJS.Timeout;
if(count >= 15) {
id = setTimeout(() => setBigSize(true), 300);
}
return function cleanup() {
clearTimeout(id);
}
});
这是测试套件:
describe('when the incrementor changes to 5 and "add" button is clicked', () => {
beforeEach(async () => {
userEvent.type(screen.getByLabelText(/Incrementor/), '{selectall}5');
userEvent.click(screen.getByRole('button', {name: "Add to Counter"}));
await screen.findByText('Current Count: 15');
})
it('renders Current Count: 15', () => {
expect(screen.getByText('Current Count: 15')).toBeInTheDocument();
});
it('renders too big and will dissapear after 300ms',async() => {
await waitForElementToBeRemoved(() => screen.queryByText(/size: small/i))
});
})
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);
这可能适用于旧版本,但对于TypeScript版本^3.5.3和Node.js版本^10.15.3,你应该能够从Timers模块中导入特定于node的函数,即:
import { setTimeout } from 'timers';
它将返回一个NodeJS类型的Timeout实例。可以传递给clearTimeout的超时:
import { clearTimeout, setTimeout } from 'timers';
const timeout: NodeJS.Timeout = setTimeout(function () { /* snip */ }, 500);
clearTimeout(timeout);