TypeScriptでsetInterval()の型が合わない理由と解決方法
@types/node に依存した状態で以下のようなコードを書いたときに Type 'Timer' is not assignable to type 'number'. というエラーメッセージが出ます。
export class ExampleClass {
intervalId: number;
constructor() {
this.intervalId = 0;
}
start() {
this.intervalId = setInterval(() => {
// do something
}, 1000);
}
stop() {
clearInterval(this.intervalId);
this.intervalId = 0;
}
}
原因
@types/node に依存している場合 setInterval() の返り値が NodeJS.Timer となるためです。@types/node に依存していなければ setInterval() の返り値は number になります。
@types/nodeの型定義: https://github.com/DefinitelyTyped/DefinitelyTyped/blob/010a8de/types/node/timers.d.ts#L73-L76- TypeScriptの
lib.dom.d.tsの型定義: https://github.com/microsoft/TypeScript/blob/3431912/lib/lib.dom.d.ts#L16739
解決方法
1. setInterval() を使う箇所で window.setInterval() とする
window オブジェクトにある setInterval() の返り値の型は number となっています。なので window.setInterval() が使える環境であれば window.setInterval() と書き換えると解決できます。
2. setInterval() の返り値を入れるプロパティの型を変更する
window.setInterval() と書き換える方法は、SSRをしているときには使えません。たとえばNext.jsを使っている場合にはエラーが出ると思われます。
この場合は以下のように intervalId の初期値に null を入れておいて、clearInterval() を実行するところで setInterval() の返り値が this.intervalId に入っているか確かめる方法があります。
export class ExampleClass {
intervalId: NodeJS.Timer | null;
constructor() {
this.intervalId = null;
}
start() {
this.intervalId = setInterval(() => {
// do something
}, 1000);
}
stop() {
if (this.intervalId) {
clearInterval(this.intervalId);
}
this.intervalId = null;
}
}
3. setInterval() 全体を Number() で囲む
关于typescript的定时器setInterval()坑_ollin2012的博客-CSDN博客_ts 定时器类型で紹介されている手法を多少改変して、setInterval() 全体を Number() で囲んで number 型にtype assertionする手法もあります。
export class ExampleClass {
intervalId: number;
constructor() {
this.intervalId = 0;
}
start() {
this.intervalId = Number(
setInterval(() => {
// do something
}, 1000),
);
}
stop() {
clearInterval(this.intervalId);
this.intervalId = 0;
}
}
まとめ
手癖のように intervalID が入るところで初期値として 0 を代入していましたが、TypeScriptではハマる可能性が高いので気をつけましょう。