[Typescript] (...args: never[]) => void

Narcoker·2025년 8월 6일

Typescript

목록 보기
12/15

개요

Nextjs + Typescript 기반의 프로젝트에서 debounce가 필요했다.

내가 만들고자 했던 것은 모든 타입의 인자를 허용하며 void 타입을 반환하는 함수와,
debounce 의 delay time(기본 값: 200)을 인자로 받아,

debounce가 적용된 함수를 반환하는 훅이었다.

그래서 다음과 같이 코드를 작성했다.

// useDebounceCallback.ts - 수정 전 
// ...args: any[]

import { useRef } from "react";

function useDebounceCallback<T extends (...args: any[]) => void>(callback: T, delay: number = 200) {
    const timeoutRef = useRef<number | null>(null);

    return (...args: Parameters<T>) => {
        if (timeoutRef.current) {
            clearTimeout(timeoutRef.current);
        }

        timeoutRef.current = window.setTimeout(() => {
            callback(...args);
        }, delay);
    };
}
// 사용 예제

const debouncedFunction = useDebounceCallback((e: UIEvent) => {
  console.log(e.target);
}, 200);

빌드 후 실행이 되지만 ...args: any[] 구문에서? eslint 경고가 발생했다.
Unexpected any. Specify a different type. eslint@typescript-eslint/no-explicit-any

eslint@typescript-eslint/no-explicit-any

TypeScript에서 any 타입 사용을 제한하는 ESLint 규칙이라고 한다.
대부분의 TypeScript 프로젝트에서는 경고 또는 오류로 설정되어 있다.

해결 방법

1. 타입이 명확한 경우

가능한 구체적인 타입을 명시 (예: string, number, CustomType 등)

function log(data: string | number | boolean) {
  console.log(data);
}

2. 타입이 불명확한 경우

unknown 사용 후 타입 가드로 처리

function process(input: unknown) {
  // input.toUpperCase(); ❌ 오류!
  
  if (typeof input === 'string') { // ✅ 타입 좁히기 후 사용 가능
    input.toUpperCase(); 
  }
}

3. 모든 타입이 필요한 경우

규칙 비활성화 (비추천)

// eslint-disable-next-line @typescript-eslint/no-explicit-any
function handle(data: any) {
  // 예외적으로 any 허용
}

모든 타입이 필요한 경우

나의 경우, callback 함수의 인자는 모든 타입을 허용해야한다.

그러나 위에서 설명한 것과 같이 비추천 이다.

말 그대로 타입스크립트 이기 때문에
any 타입을 무자비하게 허용하게 된다면
타입을 강제함으로써 코드의 안정성을 높이자는 타입스크립트의 목적을 상실하기 때문이다.

타입스크립트의 목적을 지키면서 해결하는 방법

import { useRef } from "react";

function useDebounceCallback<Args extends unknown[]>(callback: (...args: Args) => void, delay: number = 200) {
    const timeoutRef = useRef<number | null>(null);

    return (...args: Args) => {
        if (timeoutRef.current) {
            clearTimeout(timeoutRef.current);
        }

        timeoutRef.current = window.setTimeout(() => {
            callback(...args);
        }, delay);
    };
}

export default useDebounceCallback;
profile
열정, 끈기, 집념의 Frontend Developer

0개의 댓글