TypeScript - ใช้ setTimeout เวอร์ชันที่ถูกต้อง (โหนดเทียบกับหน้าต่าง)


129

ฉันทำงานเกี่ยวกับการอัพเกรดรหัส typescript setTimeoutบางเก่าที่จะใช้รุ่นคอมไพเลอร์ล่าสุดและฉันมีปัญหากับการเรียกร้องให้ รหัสคาดว่าจะเรียกใช้setTimeoutฟังก์ชันของเบราว์เซอร์ซึ่งส่งคืนตัวเลข:

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

อย่างไรก็ตามคอมไพลเลอร์กำลังแก้ไขสิ่งนี้กับการนำโหนดไปใช้แทนซึ่งจะส่งคืน NodeJS.Timer:

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

รหัสนี้ไม่ทำงานในโหนด แต่การพิมพ์ของโหนดจะถูกดึงเข้ามาเป็นการอ้างอิงกับสิ่งอื่น (ไม่แน่ใจว่าอะไร)

ฉันจะสั่งให้คอมไพเลอร์เลือกเวอร์ชันsetTimeoutที่ฉันต้องการได้อย่างไร

นี่คือรหัสที่เป็นปัญหา:

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

สิ่งนี้ก่อให้เกิดข้อผิดพลาดของคอมไพเลอร์:

TS2322: ประเภท 'Timer' ไม่สามารถกำหนดให้พิมพ์ 'number' ได้


คุณมีประเภท: ["node"] ใน tsconfig.json หรือไม่ ดูstackoverflow.com/questions/42940954/…
derkoe

@koe ไม่ฉันไม่มีตัวเลือก types: ["node"] ในไฟล์ tsconfig แต่ประเภทโหนดถูกดึงเข้ามาเป็นการพึ่งพา npm กับสิ่งอื่น
Kevin Tighe

1
คุณยังสามารถกำหนด "ประเภท" อย่างชัดเจนใน tsconfig.json - เมื่อคุณละ "node" ซึ่งจะไม่ใช้ในการคอมไพล์ เช่น "types": ["jQuery"]
derkoe

1
เป็นที่น่าแปลกใจที่คำตอบของ @koe (ใช้ตัวเลือก "ประเภท") ไม่มีคะแนนโหวตเป็นคำตอบเดียวที่ถูกต้องจริงๆ
Egor Nepomnyaschih

@ KevinTighe typesไม่ได้รวมไว้nodeแต่setTimeoutยังคงได้รับประเภทโหนดมากกว่าประเภทเบราว์เซอร์ typesค่าเริ่มต้นเป็นประเภททั้งหมดในnode_modules/@typesตามที่อธิบายไว้ในtypescriptlang.org/tsconfig#typesแต่แม้ว่าคุณจะระบุtypesและไม่รวม"node"ทำไมsetTimeoutยังคงได้รับประเภทโหนดและคุณจะรับประเภทเบราว์เซอร์ได้อย่างไร วิธีแก้ปัญหาของ @ Axke เป็นเรื่องเล็กน้อยโดยทั่วไปจะบอกว่ามันส่งคืนสิ่งที่ส่งคืน TypeScript อาจยังคงค้นหาประเภทที่ไม่ถูกต้อง แต่อย่างน้อยก็จะผิดอย่างสม่ำเสมอ
Denis Howe

คำตอบ:


101

วิธีแก้ปัญหาอื่นที่ไม่มีผลต่อการประกาศตัวแปร:

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

นอกจากนี้ควรใช้windowวัตถุอย่างชัดเจนโดยไม่ต้องany:

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

26
ฉันคิดว่าอีกอัน ( window.setTimeout) ควรเป็นคำตอบที่ถูกต้องสำหรับคำถามนี้เนื่องจากเป็นคำตอบที่ชัดเจนที่สุด
amik

5
หากคุณใช้anyประเภทนี้คุณจะไม่ได้ให้คำตอบ TypeScript จริงๆ
..

ในทำนองเดียวกันnumberประเภทจะนำไปสู่ข้อผิดพลาดของผ้าสำลีเฉพาะ TypeScript เนื่องจากsetTimeoutฟังก์ชันต้องการมากกว่านั้น
..

2
window.setTimeoutอาจทำให้เกิดปัญหากับกรอบการทดสอบหน่วย (node.js) ทางออกที่ดีที่สุดคือการใช้let n: NodeJS.Timeoutและn = setTimeout.
cchamberlain

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

clearTimeout(timer);

เมื่อใช้ReturnType<fn>คุณจะได้รับความเป็นอิสระจากแพลตฟอร์ม คุณจะไม่ถูกบังคับให้ใช้anyมิได้window.setTimeoutซึ่งจะแตกถ้าคุณเรียกใช้รหัสไม่มีเซิร์ฟเวอร์ nodeJS (เช่น. ฝั่งเซิร์ฟเวอร์หน้าแสดงผล)


ข่าวดีนี้เข้ากันได้กับ Deno!


10
ความเข้าใจของฉันอยู่ที่นี้เป็นคำตอบที่ถูกต้องและควรจะได้รับการยอมรับอย่างใดอย่างหนึ่งเนื่องจากมีการกำหนดประเภทที่เหมาะสมสำหรับทุกแพลตฟอร์มการสนับสนุนsetTimeout/ และไม่ได้ใช้clearTimeout any
afenster

12
นี่เป็นวิธีแก้ปัญหาหากคุณกำลังเขียนไลบรารีที่ทำงานบนทั้ง NodeJS และเบราว์เซอร์
yqlim

1
ประเภทผลตอบแทนคือNodeJS.Timeoutถ้าใช้setTimeoutโดยตรงและถ้าใช้number window.setTimeoutไม่ควรใช้ReturnType.
cchamberlain

@cchamberlain คุณต้องการมันเมื่อคุณเรียกใช้setTimeoutฟังก์ชันและคาดหวังว่าผลลัพธ์จะถูกเก็บไว้ในตัวแปร ลองด้วยตัวคุณเองใน TS playground
Akxe

สำหรับ OP และฉัน TypeScript setTimeoutผิดประเภท(ด้วยเหตุผลที่ไม่มีใครสามารถอธิบายได้) แต่อย่างน้อยวิธีนี้ควรปกปิดวิธีที่ดีกว่าการใช้เพียงanyเล็กน้อย
Denis Howe

15

ฉันเดาว่ามันขึ้นอยู่กับว่าคุณจะรันโค้ดของคุณที่ไหน

หากเป้าหมายรันไทม์ของคุณคือ Node JS ฝั่งเซิร์ฟเวอร์ให้ใช้:

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

หากเป้าหมายรันไทม์ของคุณคือเบราว์เซอร์ให้ใช้:

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

6

สิ่งนี้น่าจะใช้ได้กับเวอร์ชันเก่า แต่สำหรับเวอร์ชัน TypeScript ^3.5.3และเวอร์ชันNode.js ^10.15.3คุณควรจะสามารถนำเข้าฟังก์ชันเฉพาะโหนดจากโมดูลตัวจับเวลาได้เช่น:

import { setTimeout } from 'timers';

ซึ่งจะส่งคืนอินสแตนซ์ของประเภทTimeoutNodeJS.Timeoutที่คุณสามารถส่งผ่านไปยังclearTimeout:

import { clearTimeout, setTimeout } from 'timers';

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

clearTimeout(timeout);

1
ในทำนองเดียวกันหากคุณต้องการเวอร์ชันของเบราว์เซอร์setTimeoutสิ่งที่ต้องการ const { setTimeout } = windowจะล้างข้อผิดพลาดเหล่านั้น
Jack Steam

2

ฉันประสบปัญหาเดียวกันและวิธีแก้ปัญหาที่ทีมของเราตัดสินใจใช้คือใช้ "ใดก็ได้" สำหรับประเภทตัวจับเวลา เช่น:

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

มันจะทำงานกับทั้งการใช้งานของเมธอด setTimeout / setInterval / clearTimeout / clearInterval


2
ใช่มันได้ผล ฉันยังตระหนักว่าฉันสามารถระบุวิธีการบนวัตถุหน้าต่างได้โดยตรง: window.setTimeout (... ) ไม่แน่ใจว่าเป็นวิธีที่ดีที่สุดหรือไม่ แต่ฉันจะยึดติดกับมันในตอนนี้
Kevin Tighe

1
คุณสามารถนำเข้า NodeJS Namespace ใน typescript ได้อย่างถูกต้องโปรดดูคำตอบนี้
hlovdal

ในการตอบคำถามจริง ๆ ("ฉันจะสั่งให้คอมไพเลอร์เลือกเวอร์ชันที่ต้องการได้อย่างไร") คุณสามารถใช้ window.setTimeout () แทนได้ดังที่ @dhilt ตอบไว้ด้านล่าง
Anson VanDoren

window.setTimeoutอาจใช้ไม่ได้กับกรอบการทดสอบหน่วย มีแบบที่ใช้ได้NodeJS.Timeoutนะคะ.. ของ. คุณอาจคิดว่าคุณไม่ได้อยู่ในสภาพแวดล้อมโหนด แต่ฉันมีข่าวมาให้คุณ: Webpack / TypeScript เป็นต้นกำลังเรียกใช้งาน node.js
cchamberlain

0
  • หากคุณต้องการวิธีแก้ปัญหาที่แท้จริงสำหรับ typescript เกี่ยวกับตัวจับเวลาเราไปที่นี่:

    ข้อผิดพลาดอยู่ในประเภท 'number' กลับไม่ใช่ Timer หรืออย่างอื่น

    นี่สำหรับโซลูชัน typescripts เวอร์ชัน ~> 2.7:

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

ตอนนี้เราแก้ไขทั้งหมดแล้วเพียงแค่ประกาศดังนี้:

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

     ...
     }

0

หากคุณกำหนดเป้าหมายของsetInterval windowจากนั้นคุณสามารถเขียนเป็นไฟล์

let timerId: number = setInterval((()=>{
    this.populateGrid(true)
  }) as TimerHandler, 5*1000)
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.