✨회고

8/23 회고 - debounce.ts (with TypeScript)

2025. 9. 12. 15:34

오늘은 어려웠던 debounce 함수에 대해 회고를 해보려고 한다.

type Timer = ReturnType<typeof setTimeout> | null;

const debounce = <T extends (...args: Parameters<T>) => ReturnType<T>>(fn: T, timeout = 300) => {
  let timer: Timer = null;

  const debounced = (...args: Parameters<T>) => {
    if (timer) clearTimeout(timer);

    timer = setTimeout(() => {
      fn(...args);
    }, timeout);
  };

  debounced.cancel = () => {
    if (timer) {
      clearTimeout(timer);
      timer = null;
    }
  };

  return debounced as T & { cancel: () => void };
};

export default debounce;

 

 

 

그냥 자바스크립트로 구현할 때는 별로 어렵지 않았는데, 타입스크립트를 적용하니 타입 구조가 좀 복잡해져서 이 함수를 꽤 오랫동안 들여다보았다. 나는 특히 두 부분이 어려웠다.

 

1️⃣ <T extends (...args: Parameters<T>) => ReturnType<T>>(fn: T, timeout = 300) => {

먼저 T extends 문법은 <T extends string>과 같이 사용되고, 이에 따르면 위 코드는
<T extends > 안에 (...args: Parameters<T>) => ReturnType<T>가 들어간 형태이다.

  • (...args: Parameters<T>) => ReturnType<T>
    • T 함수가 받는 인자들의 타입을 배열(튜플) 형태로 추출. (여기서 T 함수 = fn 인자 함수)
    • e.g.) T가 (a: number, b: string)이라면, Parameters는 [number, string]
  • ReturnType<T>
    • T 함수가 반환하는 값의 타입을 추출. (여기서 T 함수 = fn 인자 함수)
    • e.g.) T 함수가 (a: number) => string이라면, ReturnType는 string

여기서 나는 ReturnType의 T가 debounce 함수가 리턴하는 타입을 의미하는 줄 알고 헤맸는데, 그게 아니라 fn 인자 함수를 가리킨다는 걸 몇번의 삽질을 거쳐서야 알았다.

 

2️⃣ return debounced as T & { cancel: () => void };

그냥 return debounced;로 해도 컴파일 에러가 안 뜨던데, 왜 굳이 as 타입 단언을 해줘야 되는 건지 이해가 안됐다.
그래서 제미나이한테 물어봤더니.. debounced 안에 선언된 cancel 메서드 때문이었다.

cancel 속성은 함수 선언 후에 동적으로 추가되는데, 타입스크립트 컴파일러는 이렇게 동적으로 추가되는 속성을 잘 추적하지 못한다고 한다.

 

그래서 명시적으로 T 제네릭으로부터 명확한 타입(...args: Parameters<T>) => ReturnType<T>을 상속받고, 추가로 & { cancel: () => void };를 붙여 동적 속성인 cancel 메서드의 타입까지 안전하게 보장해줘야... debounced의 cancel 메서드까지 에러 없이 사용할 수 있는 구조였다.

'✨회고' 카테고리의 다른 글

9/1 회고 - zustand와 'use client'  (0) 2025.09.12
8/29 회고 - headless ui의 Menu vs ListBox | sharp 라이브러리의 용도에 관해  (0) 2025.09.12
8/26 회고 - next.js에서의 초기 데이터 fetch + 무한 스크롤  (0) 2025.09.12
자바스크립트 리팩토링 회고🔍 - 폼 유효성 검사  (0) 2025.05.01
'✨회고' 카테고리의 다른 글
  • 9/1 회고 - zustand와 'use client'
  • 8/29 회고 - headless ui의 Menu vs ListBox | sharp 라이브러리의 용도에 관해
  • 8/26 회고 - next.js에서의 초기 데이터 fetch + 무한 스크롤
  • 자바스크립트 리팩토링 회고🔍 - 폼 유효성 검사
쥬피썬더의노예
쥬피썬더의노예
오히려 좋아
  • 쥬피썬더의노예
    d.log
    쥬피썬더의노예
    글쓰기 관리
    • 분류 전체보기 (112)
      • JS (37)
      • TS (3)
      • WEB (10)
      • React.js (20)
      • Next.js (4)
      • tanstack query (2)
      • Node.js (2)
      • HTML (5)
      • CSS (13)
      • CS (1)
      • 에이전트 (1)
      • Git (4)
      • JAVA (0)
      • SQL (0)
      • db (0)
      • GSAP (0)
      • 자료구조 (1)
      • 알고리즘 (0)
      • ✨회고 (5)
      • 포꾸 (0)
      • 인터뷰 (0)
      • 개발일지 (0)
      • 일기 (1)
      • etc (3)
      • 정처기 실기 (0)
        • C (0)
        • Java (0)
        • Python (0)
      • fonts (0)
      • articles (0)
      • 도서 (0)
  • 인기 글

  • 태그

    Next.js
    state
    폼
    리팩토링
    클로저
    TypeScript
    유효성 검사
    슬라이딩 윈도우
    상태 관리
    React Query
    프론트엔드
    SSG
    javascript
    아키텍처
    SSR
    GIT
    안티그래비티
    useState
    WEB
    CSR
    React
    자바스크립트
    Til
    리액트
    조합 패턴
    zustand
    css
    HTML
    React.JS
    useEffect
  • 최근 글

  • 전체
    오늘
    어제
  • hELLO· Designed By정상우.v4.10.3
쥬피썬더의노예
8/23 회고 - debounce.ts (with TypeScript)
상단으로

티스토리툴바