TypeScript - gunakan versi setTimeout yang benar (node ​​vs window)

129

Saya sedang mengupgrade beberapa kode TypeScript lama untuk menggunakan versi kompiler terbaru, dan saya mengalami masalah dengan panggilan ke setTimeout. Kode mengharapkan untuk memanggil fungsi browser setTimeoutyang mengembalikan angka:

setTimeout(handler: (...args: any[]) => void, timeout: number): number;

Namun, kompilator menyelesaikan ini ke implementasi node, yang mengembalikan NodeJS.Timer:

setTimeout(callback: (...args: any[]) => void, ms: number, ...args: any[]): NodeJS.Timer;

Kode ini tidak berjalan dalam node, tetapi pengetikan node ditarik sebagai ketergantungan pada sesuatu yang lain (tidak yakin apa).

Bagaimana saya dapat menginstruksikan kompilator untuk memilih versi setTimeoutyang saya inginkan?

Berikut kode yang dimaksud:

let n: number;
n = setTimeout(function () { /* snip */  }, 500);

Ini menghasilkan kesalahan kompiler:

TS2322: Ketik 'Timer' tidak dapat digunakan untuk mengetik 'nomor'.

Kevin Tighe
sumber
Apakah Anda memiliki tipe: ["node"] di tsconfig.json Anda? Lihat stackoverflow.com/questions/42940954/…
derkoe
@koe Tidak, saya tidak memiliki jenis: opsi ["node"] di file tsconfig. Tetapi jenis node ditarik sebagai ketergantungan npm ke sesuatu yang lain.
Kevin Tighe
1
Anda juga bisa secara eksplisit mendefinisikan "tipe" di tsconfig.json - saat Anda menghilangkan "node", ini tidak digunakan dalam kompilasi. misalnya "tipe": ["jQuery"]
derkoe
1
Mengejutkan bahwa jawaban @koe (gunakan opsi "jenis") tidak memiliki suara, menjadi satu-satunya jawaban yang benar.
Egor Nepomnyaschih
@ KevinTighe typestidak menyertakan nodetetapi setTimeoutmasih mendapatkan tipe Node daripada tipe browsernya. typesdefault untuk semua jenis di node_modules/@types, seperti yang dijelaskan dalam typescriptlang.org/tsconfig#types , tetapi bahkan jika Anda lakukan menentukan typesdan tidak termasuk "node", mengapa setTimeoutmasih mendapatkan jenis Node dan bagaimana Anda bisa mendapatkan jenis browser? Solusi @ Axke adalah sedikit hack, pada dasarnya mengatakan itu mengembalikan apa yang dikembalikannya. TypeScript mungkin masih menemukan jenis yang salah, tetapi setidaknya itu akan selalu salah.
Denis Howe

Jawaban:

101

Solusi lain yang tidak memengaruhi deklarasi variabel:

let n: number;
n = <any>setTimeout(function () { /* snip */  }, 500);

Juga, harus dimungkinkan untuk menggunakan windowobjek secara eksplisit tanpaany :

let n: number;
n = window.setTimeout(function () { /* snip */  }, 500);
dhilt
sumber
26
Saya pikir yang lainnya ( window.setTimeout) harus menjadi jawaban yang benar untuk pertanyaan ini karena ini adalah solusi paling jelas.
amik
5
Jika Anda menggunakan anytipe tersebut, Anda tidak benar-benar memberikan jawaban TypeScript.
S ..
demikian juga numberjenisnya akan menyebabkan kesalahan lint khusus TypeScript, karena setTimeoutfungsinya memerlukan lebih dari itu.
S ..
2
window.setTimeoutdapat menyebabkan masalah dengan kerangka pengujian unit (node.js). Solusi terbaik adalah dengan menggunakan let n: NodeJS.Timeoutdan n = setTimeout.
cchamberlain
128
let timer: ReturnType<typeof setTimeout> = setTimeout(() => { ... });

clearTimeout(timer);

Dengan menggunakan ReturnType<fn>Anda mendapatkan kemerdekaan dari platform. Anda tidak akan dipaksa untuk menggunakan anyatau window.setTimeoutyang akan rusak jika Anda menjalankan kode tanpa server nodeJS (mis. Halaman yang dirender sisi server).


Kabar baiknya, ini juga kompatibel dengan Deno!

Akxe
sumber
10
Pemahaman saya adalah bahwa ini adalah jawaban yang benar dan harus diterima, karena memberikan definisi jenis yang tepat untuk setiap platform yang mendukung setTimeout/ clearTimeoutdan tidak digunakan any.
setelah
12
Ini adalah solusinya jika Anda menulis pustaka yang berjalan di NodeJS dan browser.
yqlim
1
Jenis pengembalian adalah NodeJS.Timeoutjika menggunakan setTimeoutsecara langsung dan numberjika menggunakan window.setTimeout. Tidak perlu digunakan ReturnType.
cchamberlain
@cchamberlain Anda membutuhkannya saat Anda menjalankan setTimeoutfungsi dan mengharapkan hasilnya disimpan dalam variabel. Coba sendiri di playground TS.
Akxe
Untuk OP dan saya, TypeScript mendapatkan jenis yang setTimeoutsalah (karena alasan yang tidak dapat dijelaskan oleh siapa pun) tetapi setidaknya solusi ini harus menutupi itu dengan cara yang sedikit lebih baik daripada hanya menggunakan any.
Denis Howe
15

Saya kira itu tergantung di mana Anda akan menjalankan kode Anda.

Jika target waktu proses Anda adalah Node JS sisi server, gunakan:

let timeout: NodeJS.Timeout;
global.clearTimeout(timeout);

Jika target waktu proses Anda adalah browser, gunakan:

let timeout: number;
window.clearTimeout(timeout);
cwouter
sumber
6

Ini kemungkinan akan bekerja dengan versi yang lebih lama, tetapi dengan versi TypeScript ^3.5.3dan versi Node.js ^10.15.3, Anda harus dapat mengimpor fungsi khusus Node dari modul Timers , yaitu:

import { setTimeout } from 'timers';

Itu akan mengembalikan instance dari tipe TimeoutNodeJS.Timeout yang dapat Anda teruskan ke clearTimeout:

import { clearTimeout, setTimeout } from 'timers';

const timeout: NodeJS.Timeout = setTimeout(function () { /* snip */  }, 500);

clearTimeout(timeout);
Nick Bernard
sumber
1
Demikian pula, jika Anda menginginkan versi browser setTimeout, sesuatu seperti const { setTimeout } = windowakan menghapus kesalahan tersebut.
Jack Steam
2

Saya menghadapi masalah yang sama dan solusi yang diputuskan oleh tim kami untuk digunakan, hanya menggunakan "apa saja" untuk jenis pengatur waktu. Misalnya:

let n: any;
n = setTimeout(function () { /* snip */  }, 500);

Ini akan bekerja dengan kedua implementasi metode setTimeout / setInterval / clearTimeout / clearInterval.

Mark Dolbyrev
sumber
2
Ya, itu berhasil. Saya juga menyadari bahwa saya dapat menentukan metode pada objek jendela secara langsung: window.setTimeout (...). Tidak yakin apakah itu cara terbaik untuk pergi, tetapi saya akan tetap menggunakannya untuk saat ini.
Kevin Tighe
1
Anda dapat mengimpor namespace NodeJS dengan benar dalam skrip ketikan, lihat jawaban ini .
hlovdal
Untuk benar-benar menjawab pertanyaan ("bagaimana saya dapat menginstruksikan compiler untuk memilih versi yang saya inginkan"), Anda dapat menggunakan window.setTimeout () sebagai gantinya, seperti yang dijawab @dhilt di bawah ini.
Anson VanDoren
window.setTimeoutmungkin tidak bekerja dengan kerangka pengujian unit. Ada tipe yang bisa digunakan disini .. Ini NodeJS.Timeout. Anda mungkin berpikir Anda tidak berada dalam lingkungan node tetapi saya punya berita untuk Anda: Webpack / TypeScript dll. Sedang menjalankan node.js.
cchamberlain
0
  • Jika Anda ingin solusi nyata untuk skrip ketikan tentang pengatur waktu, ini dia:

    Bug dalam jenis 'nomor' kembali itu bukan Timer atau apa pun.

    Ini untuk solusi ketikan versi ~> 2.7:

export type Tick = null | number | NodeJS.Timer;

Sekarang kita perbaiki semua, cukup nyatakan seperti ini:

 import { Tick } from "../../globals/types";

 export enum TIMER {
    INTERVAL = "INTERVAL",
    TIMEOUT = "TIMEOUT", 
 };

 interface TimerStateI {
   timeInterval: number;
 }

 interface TimerI: TimerStateI {
   type: string;
   autoStart: boolean;
   isStarted () : bool;
 }

     class myTimer extends React.Component<TimerI, TimerStateI > {

          private myTimer: Tick = null;
          private myType: string = TIMER.INTERVAL;
          private started: boll = false;

          constructor(args){
             super(args);
             this.setState({timeInterval: args.timeInterval});

             if (args.autoStart === true){
               this.startTimer();
             }
          }

          private myTick = () => {
            console.log("Tick");
          }    

          private startTimer = () => {

            if (this.myType === TIMER.INTERVAL) {
              this.myTimer = setInterval(this.myTick, this.timeInterval);
              this.started = true;
            } else if (this.myType === TIMER.TIMEOUT) {
              this.myTimer = setTimeout(this.myTick, this.timeInterval);
              this.started = true;
            }

          }

         private isStarted () : bool {
           return this.started;
         }

     ...
     }
Nikola Lukic
sumber
0

Jika Anda menargetkan setIntervaldari window. Kemudian Anda juga bisa menulisnya sebagai

let timerId: number = setInterval((()=>{
    this.populateGrid(true)
  }) as TimerHandler, 5*1000)
}
सत्यमेव जयते
sumber