programing

TypeScript - 올바른 버전의 setTimeout 사용(노드와 창)

mailnote 2023. 4. 6. 21:52
반응형

TypeScript - 올바른 버전의 setTimeout 사용(노드와 창)

최신 버전의 컴파일러를 사용하기 위해 오래된 TypeScript 코드를 업그레이드하고 있는데 호출에 문제가 있습니다.setTimeout코드에서는 브라우저의 콜을 상정하고 있습니다.setTimeout숫자를 반환하는 함수:

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

단, 컴파일러는 노드 구현으로 이 문제를 해결하고 NodeJs를 반환합니다.타이머:

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

이 코드는 노드에서는 실행되지 않지만 노드 타입은 다른 것에 대한 종속성(무엇에 대한 의존성)으로 인식되고 있습니다.

컴파일러에 명령어 버전 선택을 지시하려면 어떻게 해야 합니까?setTimeout내가 원하는 거?

문제의 코드는 다음과 같습니다.

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

이로 인해 컴파일러 오류가 발생합니다.

TS2322: '타이머' 유형은 '번호' 유형에 할당할 수 없습니다.

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

clearTimeout(timer);

사용방법ReturnType<fn>플랫폼으로부터 독립하는 것입니다.둘 다 사용하지 않아도 됩니다.any도 아니다window.setTimeout노드상에서 코드를 실행하면, 파손됩니다.JS 서버(예: 서버측 렌더링 페이지).


좋은 소식입니다, 이것도 Deno에 대응하고 있습니다!

2021년 갱신

Akxe의 답변에 따르면ReturnType<Type>타입스크립트 2.3에 도입된 기술:

let n: ReturnType<typeof setTimeout>;
n = setTimeout(cb, 500);

그것은 좋고 명시적인 캐스팅보다 선호되는 것처럼 보인다.단, 이 경우 n의 결과 유형은 NodeJS입니다.Timeout"은 다음과 같이 사용할 수 있습니다.

let n: NodeJS.Timeout;
n = setTimeout(cb, 500);

Return Type/Node JS의 유일한 문제타임아웃 어프로치란 브라우저 고유의 환경에서 수치 조작을 실시하려면 여전히 캐스팅이 필요하다는 것입니다.

if ((n as unknown as number) % 2 === 0) {
  clearTimeout(n);
}

원답

변수 선언에 영향을 주지 않는 회피책:

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

또한 브라우저 고유의 환경에서는window캐스팅이 없는 객체:

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

어디서 코드를 실행하느냐에 따라 다르겠죠.

런타임 대상이 서버 측 노드 JS인 경우 다음을 사용합니다.

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

런타임 대상이 브라우저인 경우 다음을 사용합니다.

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

이것은 나에게 완벽하게 잘 작동한다.

type Timer = ReturnType<typeof setTimeout>

const timer: Timer = setTimeout(() => {}, 1000)

이 기능은 이전 버전에서는 작동하지만 TypeScript 버전에서는 작동합니다.^3.5.3및 Node.js 버전^10.15.3Timers 모듈에서 노드 고유의 기능을 Import할 수 있습니다.즉, 다음과 같습니다.

import { setTimeout } from 'timers';

그러면 Timeout of type의 인스턴스가 반환됩니다.NodeJS.Timeout넘겨줄 수 있는clearTimeout:

import { clearTimeout, setTimeout } from 'timers';

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

clearTimeout(timeout);

또, 다음의 사양에 대해서도 언급하고 싶다.NodeJS.Timeout포함한다[Symbol.toPrimitive](): number:

interface Timeout extends Timer {
    /**
     * If true, the `Timeout` object will keep the Node.js event loop active.
     * @since v11.0.0
     */
    hasRef(): boolean;
    /**
     * Sets the timer's start time to the current time, and reschedules the timer to
     * call its callback at the previously specified duration adjusted to the current
     * time. This is useful for refreshing a timer without allocating a new
     * JavaScript object.
     *
     * Using this on a timer that has already called its callback will reactivate the
     * timer.
     * @since v10.2.0
     * @return a reference to `timeout`
     */
    refresh(): this;
    [Symbol.toPrimitive](): number;
}

또한 호환성을 위해 노드의 다른 타임아웃 API는 플레인 정수 ID에서 정상적으로 작동하므로 개체를 수락할 필요가 없습니다.오브젝트는 "서버"측에서 사용되며 프로세스 활성 및 가비지 수집을 보다 세밀하게 제어할 수 있습니다.예를 들어 다음과 같습니다.

function clearTimeout(timeoutId: NodeJS.Timeout | string | number | undefined): void;

, ,, 음, 음, 음, 다, 다, 다, 다, 다, cast, cast, cast, cast, cast, cast, cast, cast, this, this, this, cast, this, ,, cast, cast, cast, on, this, this, setTimeout ★★★★★★★★★★★★★★★★★」setInterval:

let timeoutId: number | undefined;
timeoutId = Number(setTimeout(callback, ms));

function clear() {
  clearTimeout(timeoutId);
}

어떤 API와도 충돌하지 않습니다.다만, 나중에 다른 API 계약에 대한 초기값으로 의존할 필요가 있는지 여부에 대해서는 문제가 되지 않습니다.

setIntervalwindow 쓸 도 있어요.

let timerId: number = setInterval((()=>{
    this.populateGrid(true)
  }) as TimerHandler, 5*1000)
}

나는 이 문제를 세팅으로 해결했다.

tsconfig.json:
{
  "compilerOptions": {
    "skipLibCheck": true,
  }
}

그리고 .d.ts를 만듭니다.

*.d.ts:
declare namespace NodeJS {
    type Timeout = number;
    type Timer = number;
}

타이프 스크립트버전 4.2.3

리액트를 사용하고 있었는데, 같은 문제가 있어서 다음과 같이 해결했습니다.

import React, { useRef, useState, useEffect} from 'react';
import { Alert } from '../types/alerts';

const AlertComponent: React.FC<{alert: Alert}> = ({alert}) => {
  const intervalRef = useRef<NodeJS.Timeout>();
  const [count, setCount] = useState(alert.timeLimit)

  useEffect(() => {
    intervalRef.current = setInterval(
      () => {setCount((count) => count - 1)},
      1000
    )

    return () => {
      clearInterval(intervalRef.current as NodeJS.Timeout)
    }
  }, [])

  return (
    <p>{count}</p>
  )
}

export default AlertComponent;

에 useEffect()가 있습니다.clearInterval(intervalRef.current as NodeJS.Timeout) clear" "를 명시적으로 때문입니다.NodeJ는 NodeJ를 사용합니다.[ Timeout ] | undefined ]미정의입니다.

RTL을 사용하여 Counter 앱을 테스트하고 있었습니다.구체적으로는 카운트가 15가 되면 삭제되는 요소를 테스트하고 있었습니다.테스트 실행 후 컴포넌트가 파기되기 때문에 setTimeout은 이후에도 계속 실행되며 React는 마운트 해제된 컴포넌트에 대해 상태 업데이트를 수행할 수 없다는 오류가 발생합니다.따라서 dhilt의 답변을 바탕으로 다음과 같이 useEffect 청소 기능을 수정할 수 있었습니다.

const [count, setCount] = useState(initialCount);
const [bigSize, setBigSize] = useState(initialCount >= 15);

useEffect(() => {
    let id: NodeJS.Timeout;

    if(count >= 15) {
        id = setTimeout(() => setBigSize(true), 300);
    }

    return function cleanup() {
        clearTimeout(id);
    }
});

테스트 스위트는 다음과 같습니다.

describe('when the incrementor changes to 5 and "add" button is clicked', () => {
        beforeEach(async () => {
            userEvent.type(screen.getByLabelText(/Incrementor/), '{selectall}5');
            userEvent.click(screen.getByRole('button', {name: "Add to Counter"}));
            await screen.findByText('Current Count: 15');
        })
            
        it('renders Current Count: 15', () => {
            expect(screen.getByText('Current Count: 15')).toBeInTheDocument();
        });
        
        it('renders too big and will dissapear after 300ms',async() => {
            await waitForElementToBeRemoved(() => screen.queryByText(/size: small/i))
        });
        
    })

TS2322: '타이머' 유형은 '번호' 유형에 할당할 수 없습니다.

심플한 솔루션

abc : any;

애초에

abc = setInterval 또는 abc = setTimeout

저도 같은 문제에 직면했고, 저희 팀이 사용하기로 결정한 회피책은 타이머 유형에 "임의"를 사용하는 것이었습니다.예:

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

set Timeout/set의 양쪽 구현에서 동작합니다.인터벌/clearTimeout/clear인터벌 메서드

언급URL : https://stackoverflow.com/questions/45802988/typescript-use-correct-version-of-settimeout-node-vs-window

반응형