TypeScript: use la versión correcta de setTimeout (nodo vs ventana)


117

Estoy trabajando para actualizar un código TypeScript antiguo para usar la última versión del compilador y tengo problemas con una llamada a setTimeout. El código espera llamar a la setTimeoutfunción del navegador que devuelve un número:

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

Sin embargo, el compilador está resolviendo esto en la implementación del nodo, que devuelve un NodeJS.Timer:

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

Este código no se ejecuta en el nodo, pero los tipos de nodos se incorporan como una dependencia de otra cosa (no estoy seguro de qué).

¿Cómo puedo indicarle al compilador que elija la versión setTimeoutque quiero?

Aquí está el código en cuestión:

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

Esto produce el error del compilador:

TS2322: El tipo 'Temporizador' no se puede asignar al tipo 'número'.


¿Tiene tipos: ["nodo"] en su tsconfig.json? Ver stackoverflow.com/questions/42940954/…
derkoe

@koe No, no tengo la opción de tipos: ["nodo"] en el archivo tsconfig. Pero los tipos de nodos se incorporan como una dependencia de npm a otra cosa.
Kevin Tighe

1
También puede definir explícitamente "tipos" en tsconfig.json; cuando omite "nodo", no se usa en la compilación. por ejemplo, "tipos": ["jQuery"]
derkoe

1
Es sorprendente que la respuesta de @koe (use la opción "tipos") no tenga ningún voto, siendo la única respuesta correcta verdadera.
Egor Nepomnyaschih

@ KevinTighe typesno incluye nodepero setTimeoutaún obtiene su tipo de nodo en lugar de su tipo de navegador. typespor defecto a todos los tipos de node_modules/@types, como se explica en typescriptlang.org/tsconfig#types , pero incluso si lo hace especificar typesy no incluye "node", ¿por qué setTimeoutaún así obtener su tipo de nodo y cómo se puede obtener el tipo de navegador? La solución de @ Axke es un truco, básicamente dice que devuelve lo que devuelve. Es posible que TypeScript siga encontrando el tipo incorrecto, pero al menos siempre será incorrecto.
Denis Howe

Respuestas:


88

Otra solución alternativa que no afecta la declaración de variables:

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

Además, debería ser posible utilizar el windowobjeto explícitamente sin any:

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

25
Creo que el otro ( window.setTimeout) debería ser la respuesta correcta para esta pregunta, ya que es la solución más clara.
amik

5
Si está utilizando el anytipo, realmente no está dando una respuesta de TypeScript.
S ..

del mismo modo, el numbertipo dará lugar a errores de pelusa específicos de TypeScript, ya que la setTimeoutfunción requiere más que eso.
S ..

2
window.setTimeoutpuede causar problemas con los marcos de prueba unitarios (node.js). La mejor solución es usar let n: NodeJS.Timeouty n = setTimeout.
cchamberlain

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

clearTimeout(timer);

Al usarlo ReturnType<fn>, obtiene independencia de la plataforma. No se verá obligado a usar anyni window.setTimeoutque se romperá si ejecuta el código sin servidor nodeJS (por ejemplo, página renderizada del lado del servidor).


Buenas noticias, ¡esto también es compatible con Deno!


10
Mi entendimiento es que esta es la respuesta correcta y debe ser la aceptada, ya que proporciona la definición del tipo adecuado para cada plataforma de apoyo setTimeout/ clearTimeouty no utiliza any.
afenster

12
Esta es la solución si está escribiendo una biblioteca que se ejecuta tanto en NodeJS como en el navegador.
yqlim

El tipo de retorno es NodeJS.Timeoutsi se usa setTimeoutdirectamente y numbersi se usa window.setTimeout. No debería necesitar usarlo ReturnType.
cchamberlain

@cchamberlain Lo necesita mientras ejecuta la setTimeoutfunción y espera que su resultado se almacene en la variable. Pruébelo usted mismo en TS playground.
Akxe

Para el OP y para mí, TypeScript tiene el tipo de setTimeouterror (por razones que nadie puede explicar) pero al menos esta solución debería enmascarar eso de una manera un poco mejor que simplemente usar any.
Denis Howe

16

Supongo que depende de dónde ejecutará su código.

Si su destino de tiempo de ejecución es el nodo JS del lado del servidor, use:

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

Si su objetivo de tiempo de ejecución es un navegador, use:

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

5

Es probable que esto funcione con versiones anteriores, pero con la versión TypeScript ^3.5.3y la versión Node.js ^10.15.3, debería poder importar las funciones específicas de Node desde el módulo Timers , es decir:

import { setTimeout } from 'timers';

Eso devolverá una instancia de Timeout de tipo NodeJS.Timeoutque puede pasar a clearTimeout:

import { clearTimeout, setTimeout } from 'timers';

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

clearTimeout(timeout);

1
Del mismo modo, si desea la versión del navegador de setTimeout, algo como const { setTimeout } = windowaclarará esos errores.
Jack Steam

3

Enfrenté el mismo problema y la solución alternativa que nuestro equipo decidió usar fue simplemente usar "cualquiera" para el tipo de temporizador. P.ej:

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

Funcionará con ambas implementaciones de los métodos setTimeout / setInterval / clearTimeout / clearInterval.


2
Sí, eso funciona. También me di cuenta de que puedo especificar el método directamente en el objeto de la ventana: window.setTimeout (...). No estoy seguro de si esa es la mejor manera de hacerlo, pero me quedaré con ella por ahora.
Kevin Tighe

1
Puede importar el espacio de nombres NodeJS correctamente en mecanografiado, consulte esta respuesta .
hlovdal

Para responder realmente a la pregunta ("cómo puedo indicarle al compilador que elija la versión que quiero"), puede usar window.setTimeout () en su lugar, como @dhilt respondió a continuación.
Anson VanDoren

window.setTimeoutpuede no funcionar con marcos de prueba unitarios. Hay un tipo que se puede utilizar aquí .. Su NodeJS.Timeout. Puede pensar que no está en un entorno de nodo, pero tengo noticias para usted: Webpack / TypeScript, etc.están ejecutando node.js.
cchamberlain

1
  • Si desea una solución real para mecanografiar sobre temporizadores, aquí vamos:

    El error está en el tipo de retorno 'número', no es Timer ni nada más.

    Esto es para la solución mecanografiada versión ~> 2.7:

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

Ahora arreglamos todo, solo declaramos así:

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

     ...
     }
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.