TypeScript - Verwenden Sie die richtige Version von setTimeout (Knoten gegen Fenster).

109

Ich arbeite daran, alten TypeScript-Code auf die neueste Compiler-Version zu aktualisieren, und habe Probleme mit einem Aufruf von setTimeout. Der Code erwartet, die Browserfunktion aufzurufen, die setTimeouteine Nummer zurückgibt:

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

Der Compiler löst dies jedoch stattdessen in der Knotenimplementierung auf, die einen NodeJS.Timer zurückgibt:

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

Dieser Code wird nicht im Knoten ausgeführt, aber die Knotentypisierungen werden als Abhängigkeit von etwas anderem abgerufen (nicht sicher, was).

Wie kann ich den Compiler anweisen, die setTimeoutgewünschte Version auszuwählen ?

Hier ist der fragliche Code:

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

Dies erzeugt den Compilerfehler:

TS2322: Der Typ 'Timer' kann nicht dem Typ 'Nummer' zugewiesen werden.

Kevin Tighe
quelle
Haben Sie einen Typ: ["Knoten"] in Ihrer tsconfig.json? Siehe stackoverflow.com/questions/42940954/…
koe
@koe Nein, ich habe nicht die Option types: ["node"] in der tsconfig-Datei. Aber die Knotentypen werden als npm-Abhängigkeit von etwas anderem eingezogen.
Kevin Tighe
1
Sie können in tsconfig.json auch explizit "Typen" definieren. Wenn Sie "Knoten" weglassen, wird dies bei der Kompilierung nicht verwendet. zB "Typen": ["jQuery"]
koe
1
Es ist überraschend, dass die Antwort von @koe (Option "Typen" verwenden) keine Stimmen hat, da dies die einzig richtige Antwort ist.
Egor Nepomnyaschih

Antworten:

86

Eine weitere Problemumgehung, die sich nicht auf die Variablendeklaration auswirkt:

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

Es sollte auch möglich sein, das windowObjekt explizit zu verwenden, ohne any:

let n: number;
n = window.setTimeout(function () { /* snip */  }, 500);
Dhilt
quelle
24
Ich denke, das andere ( window.setTimeout) sollte die richtige Antwort auf diese Frage sein, da es die klarste Lösung ist.
Amik
5
Wenn Sie den anyTyp verwenden, geben Sie keine TypeScript-Antwort.
S ..
Ebenso führt der numberTyp zu TypeScript-spezifischen Flusenfehlern, da die setTimeoutFunktion mehr erfordert.
S ..
1
window.setTimeoutkann Probleme mit Unit-Test-Frameworks (node.js) verursachen. Die beste Lösung ist let n: NodeJS.Timeoutund n = setTimeout.
Kammerherr
99
let timer: ReturnType<typeof setTimeout> = setTimeout(() => { ... });

clearTimeout(timer);

Durch die Verwendung erhalten ReturnType<fn>Sie Unabhängigkeit von der Plattform. Sie werden nicht gezwungen zu verwenden anyoder window.setTimeoutwas wird brechen, wenn Sie den Code no nodeJS Server ausführen (z. B. serverseitig gerenderte Seite).


Gute Nachrichten, das ist auch mit Deno kompatibel!

Akxe
quelle
9
Mein Verständnis ist, dass dies die richtige Antwort ist und akzeptiert werden sollte, da es die richtige Typdefinition für jede Plattform bietet, die setTimeout/ unterstützt clearTimeoutund nicht verwendet any.
afenster
11
Dies ist die Lösung, wenn Sie eine Bibliothek schreiben, die sowohl auf NodeJS als auch im Browser ausgeführt wird.
Yqlim
Der Rückgabetyp ist NodeJS.Timeoutbei setTimeoutdirekter Verwendung und numberbei Verwendung window.setTimeout. Sollte nicht nötig sein ReturnType.
Kammerherr
@cchamberlain Sie benötigen es, wenn Sie die setTimeoutFunktion ausführen und erwarten, dass das Ergebnis in der Variablen gespeichert wird. Probieren Sie es selbst auf dem TS-Spielplatz.
Akxe
14

Ich denke, es hängt davon ab, wo Sie Ihren Code ausführen werden.

Wenn Ihr Laufzeitziel der serverseitige Node JS ist, verwenden Sie:

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

Wenn Ihr Laufzeitziel ein Browser ist, verwenden Sie:

let timeout: number;
window.clearTimeout(timeout);
cwouter
quelle
4

Ich hatte das gleiche Problem und die Problemumgehung, für die sich unser Team entschieden hat, bestand darin, "any" für den Timer-Typ zu verwenden. Z.B:

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

Es funktioniert mit beiden Implementierungen der Methoden setTimeout / setInterval / clearTimeout / clearInterval.

Mark Dolbyrev
quelle
2
Ja, das funktioniert. Mir wurde auch klar, dass ich die Methode einfach direkt für das Fensterobjekt angeben kann: window.setTimeout (...). Ich bin mir nicht sicher, ob das der beste Weg ist, aber ich bleibe vorerst dabei.
Kevin Tighe
1
Sie können den NodeJS-Namespace ordnungsgemäß in Typoskript importieren. Siehe diese Antwort .
Hlovdal
Um die Frage tatsächlich zu beantworten ("Wie kann ich den Compiler anweisen, die gewünschte Version auszuwählen"), können Sie stattdessen window.setTimeout () verwenden, wie @dhilt unten beantwortet.
Anson VanDoren
window.setTimeoutfunktioniert möglicherweise nicht mit Unit-Test-Frameworks. Es gibt einen Typ, der hier verwendet werden kann NodeJS.Timeout. Sie denken vielleicht, Sie befinden sich nicht in einer Knotenumgebung, aber ich habe Neuigkeiten für Sie: Webpack / TypeScript usw. führen node.js aus.
Kammerherr
4

Dies wird wahrscheinlich mit älteren Versionen funktionieren, aber mit TypeScript-Version ^3.5.3und Node.js-Version ^10.15.3sollten Sie in der Lage sein, die knotenspezifischen Funktionen aus dem Timer- Modul zu importieren , dh:

import { setTimeout } from 'timers';

Dadurch wird eine Instanz von Timeout vom Typ zurückgegeben NodeJS.Timeout, an die Sie übergeben können clearTimeout:

import { clearTimeout, setTimeout } from 'timers';

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

clearTimeout(timeout);
Nick Bernard
quelle
Wenn Sie die Browserversion von möchten , werden diese Fehler durch so setTimeoutetwas wie const { setTimeout } = windowbehoben.
Jack Steam
1
  • Wenn Sie hier eine echte Lösung für Typoskripte über Timer suchen, gehen wir wie folgt vor:

    Fehler ist im Rückgabetyp 'Nummer', es ist kein Timer oder irgendetwas anderes.

    Dies ist für Typoskripte Version ~> 2.7 Lösung:

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

Jetzt haben wir alle behoben, erklären Sie einfach so:

 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
quelle