TypeScript: utilizza la versione corretta di setTimeout (nodo vs finestra)


116

Sto lavorando all'aggiornamento di un vecchio codice TypeScript per utilizzare l'ultima versione del compilatore e ho problemi con una chiamata a setTimeout. Il codice si aspetta di chiamare la setTimeoutfunzione del browser che restituisce un numero:

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

Tuttavia, il compilatore risolve invece questo problema nell'implementazione del nodo, che restituisce un NodeJS.Timer:

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

Questo codice non viene eseguito nel nodo, ma le tipizzazioni del nodo vengono inserite come dipendenza da qualcos'altro (non so cosa).

Come posso istruire il compilatore a scegliere la versione setTimeoutche desidero?

Ecco il codice in questione:

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

Questo produce l'errore del compilatore:

TS2322: il tipo "Timer" non è assegnabile al tipo "numero".


Hai un tipo: ["nodo"] nel tuo tsconfig.json? Vedi stackoverflow.com/questions/42940954/…
koe

@koe No, non ho i tipi: opzione ["node"] nel file tsconfig. Ma i tipi di nodo vengono inseriti come dipendenza npm da qualcos'altro.
Kevin Tighe

1
Puoi anche definire esplicitamente "tipi" in tsconfig.json - quando ometti "nodo" non viene utilizzato nella compilazione. ad esempio "types": ["jQuery"]
koe

1
È sorprendente che la risposta di @koe (usa l'opzione "types") non abbia voti, essendo l'unica vera risposta corretta.
Egor Nepomnyaschih

@ KevinTighe typesnon include nodema setTimeoutottiene comunque il suo tipo di nodo piuttosto che il suo tipo di browser. typesdefault a tutti i tipi di node_modules/@types, come spiegato in typescriptlang.org/tsconfig#types , ma anche se si fa indicare typese non si include "node", perché non setTimeoutancora ottenere il tipo di nodo e come è possibile ottenere il tipo di browser? La soluzione di @ Axke è un po 'un trucco, in pratica dicendo che restituisce ciò che restituisce. TypeScript potrebbe ancora trovare il tipo sbagliato, ma almeno sarà costantemente sbagliato.
Denis Howe,

Risposte:


88

Un'altra soluzione alternativa che non influisce sulla dichiarazione delle variabili:

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

Inoltre, dovrebbe essere possibile utilizzare l' windowoggetto esplicitamente senza any:

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

25
Penso che l'altra ( window.setTimeout) dovrebbe essere la risposta corretta a questa domanda in quanto è la soluzione più chiara.
amik

5
Se stai usando il anytipo, non stai davvero dando una risposta TypeScript.
S ..

allo stesso modo il numbertipo porterà a errori di lint specifici di TypeScript, poiché la setTimeoutfunzione richiede più di questo.
S ..

2
window.setTimeoutpuò causare problemi con i framework di unit test (node.js). La soluzione migliore è usare let n: NodeJS.Timeoute n = setTimeout.
cchamberlain

112
let timer: ReturnType<typeof setTimeout> = setTimeout(() => { ... });

clearTimeout(timer);

Usando ReturnType<fn>ottieni l'indipendenza dalla piattaforma. Non sarai costretto a usare anywindow.setTimeoutquale si interromperà se esegui il codice no nodeJS server (es. Pagina renderizzata lato server).


Buone notizie, anche questo è compatibile con Deno!


10
La mia comprensione è che questa è la risposta giusta e dovrebbe essere quella accettata, poiché fornisce la giusta definizione del tipo per ogni piattaforma che supporta setTimeout/ clearTimeoute non utilizza any.
dopo il

12
Questa è la soluzione se stai scrivendo una libreria che funziona sia su NodeJS che sul browser.
yqlim

Il tipo restituito è NodeJS.Timeoutse si utilizza setTimeoutdirettamente e numberse si utilizza window.setTimeout. Non dovrebbe essere necessario utilizzare ReturnType.
cchamberlain

@cchamberlain Ne hai bisogno mentre esegui la setTimeoutfunzione e ti aspetti che il suo risultato venga memorizzato nella variabile. Provalo tu stesso nel parco giochi TS.
Akxe

Per l'OP e per me, TypeScript ottiene il tipo di setTimeoutsbagliato (per ragioni che nessuno può spiegare) ma almeno questa soluzione dovrebbe mascherarlo in un modo leggermente migliore rispetto al semplice utilizzo any.
Denis Howe,

15

Immagino che dipenda da dove eseguirai il codice.

Se la destinazione del runtime è Node JS lato server, utilizza:

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

Se la tua destinazione di runtime è un browser, utilizza:

let timeout: number;
window.clearTimeout(timeout);

5

Questo probabilmente funzionerà con le versioni precedenti, ma con la versione TypeScript ^3.5.3e la versione Node.js ^10.15.3, dovresti essere in grado di importare le funzioni specifiche del nodo dal modulo Timers , ovvero:

import { setTimeout } from 'timers';

Ciò restituirà un'istanza di Timeout di tipo a NodeJS.Timeoutcui puoi passare clearTimeout:

import { clearTimeout, setTimeout } from 'timers';

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

clearTimeout(timeout);

1
Allo stesso modo, se desideri la versione browser di setTimeout, qualcosa di simile const { setTimeout } = windoweliminerà quegli errori.
Jack Steam

4

Ho affrontato lo stesso problema e la soluzione alternativa che il nostro team ha deciso di utilizzare è stata quella di utilizzare "qualsiasi" per il tipo di timer. Per esempio:

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

Funzionerà con entrambe le implementazioni dei metodi setTimeout / setInterval / clearTimeout / clearInterval.


2
Sì, funziona. Mi sono anche reso conto che posso semplicemente specificare il metodo direttamente sull'oggetto finestra: window.setTimeout (...). Non sono sicuro che sia il modo migliore per andare, ma per ora lo terrò.
Kevin Tighe

1
Puoi importare correttamente lo spazio dei nomi NodeJS nel dattiloscritto, vedi questa risposta .
hlovdal

Per rispondere effettivamente alla domanda ("come posso istruire il compilatore a scegliere la versione che desidero"), puoi invece usare window.setTimeout (), come ha risposto @dhilt di seguito.
Anson VanDoren

window.setTimeoutpotrebbe non funzionare con i framework di unit test. C'è un tipo che può essere usato qui .. Il suo NodeJS.Timeout. Potresti pensare di non essere in un ambiente nodo, ma ho una notizia per te: Webpack / TypeScript ecc. Stanno eseguendo node.js.
cchamberlain

1
  • Se vuoi una vera soluzione per il dattiloscritto sui timer, eccoci qui:

    Il bug è nel tipo di ritorno "numero", non è Timer o altro.

    Questo è per la versione dei dattiloscritti ~> 2.7 soluzione:

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

Ora abbiamo sistemato tutto, basta dichiarare in questo modo:

 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;
         }

     ...
     }
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.