以下の記事のdebounce関数を使用したいと思ったのですが、使用する環境はTypescriptなもんでそのままコピできません。コピペで仕事ができない辛い時代になってきました。
なので調整してみました。
完成品はこちら
function debounce<T, F extends (...args: any[]) => void>(
thisArg: T,
func: F,
timeout = 300
): (...args: Parameters<F>) => void {
let timer: ReturnType<typeof setTimeout>;
return (...args: Parameters<F>): void => {
clearTimeout(timer);
timer = setTimeout(() => {
func.apply(thisArg, args);
}, timeout);
};
}
function saveInput(): void {
console.log('Saving data');
}
const processChange: (...args: any[]) => void = debounce(undefined, () => saveInput());
Q. どうやって解決したの?
A. ChatGPT です。
といってもそのまま「Tyepscriptに変換して!」ってお願いしても、一発でエラーのないコードは生成されないもんです。
最初にいただいたコードはこちら。
function debounce<F extends (...args: any[]) => void>(func: F, timeout = 300): (...args: Parameters<F>) => void {
let timer: number | undefined;
return (...args: Parameters<F>): void => {
clearTimeout(timer);
timer = setTimeout(() => {
func.apply(this, args);
}, timeout);
};
}
function saveInput(): void {
console.log('Saving data');
}
const processChange: (...args: any[]) => void = debounce(() => saveInput());
このままだとtypescriptエラーが発生します。
setTimeout の返り値の型
まず timer = setTimeout(() => {
の行で、
Type 'Timeout' is not assignable to type 'number'.ts(2322)
というエラーが発生。調べてみると、環境によって number
だったり NodeJS.Timeout
だったりするので、Typescriptに型推論させたほうが良いとのこと。
なので
- let timer: number | undefined;
+ let timer: ReturnType<typeof setTimeout>;
とする。
this
の型
次に func.apply(this, args);
の行で
'this' implicitly has type 'any' because it does not have a type annotation.ts(2683)
というエラーが発生しました。
this を undefined にしたり
- func.apply(this, args);
+ func.apply(undefined, args);
this の型を 明示的に any にしたり
- return (...args: Parameters<F>): void => {
+ return function (this: any, ...args: Parameters<F>): void {
のようにしてもいいかなーと思ったのですが、せっかく apply メソッドを使っているのでちゃんと活用したいところ。
ChatGPTさんが、ジェネリクスを使いなさいと、コードまでくださったので、その通りにします。
- function debounce<F extends (...args: any[]) => void>(
- func: F,
- timeout = 300
- ): (...args: Parameters<F>) => void {
...
- func.apply(this, args);
...
- const processChange: (...args: any[]) => void = debounce(() => saveInput());
+ function debounce<T, F extends (...args: any[]) => void>(
+ thisArg: T,
+ func: F,
+ timeout = 300
+ ): (...args: Parameters<F>) => void {
...
+ func.apply(thisArg, args);
...
+ const processChange: (...args: any[]) => void = debounce(undefined, () => saveInput());
呼び出す側で thisArg を指定する必要がありますが、オブジェクトのメソッドでも使用したいのでこのようにする。
おまけ
<button onclick="processChange()">Click me</button>
のように、onclickハンドラ内で直接processChange
を呼び出すときは、以下のようにしてグローバル変数に登録をする。
あまりこういうことしたことがないので、備忘録。
(window as any).processChange = debounce(window, () => saveInput());
参考記事
setTimeoutの型注釈がnumber型ではエラーになる理由 #React - Qiita
this - JavaScript | MDNMDN Web DocsMDN logoMozilla logo