TypeScript 4.2의 Leading/Middle Rest Elements in Tuple Types

문제 상황

프로젝트에서 로깅 유틸리티를 만드는 중이었다. 타임스탬프, 로그 레벨은 고정이고 나머지는 가변 인자로 받아야 했는데, TypeScript 4.1까지는 튜플의 rest element가 마지막에만 올 수 있어서 타입 정의가 불가능했다.

// 원하는 형태였지만 4.1에서는 불가능
type LogArgs = [timestamp: string, level: string, ...messages: any[]];

TypeScript 4.2 업데이트

2월에 릴리즈된 4.2에서 rest element를 튜플의 앞이나 중간에도 배치할 수 있게 됐다. 바로 프로젝트를 4.2로 올렸다.

// 이제 가능해진 패턴들
type LogArgs = [timestamp: string, ...messages: any[], level: string];
type Middleware = [...args: any[], next: () => void];

function log(...args: LogArgs) {
  const [timestamp, ...rest] = args;
  const level = rest.pop();
  console.log(`[${timestamp}] [${level}]`, ...rest);
}

실전 활용

API 미들웨어 체인 타입을 정의할 때 특히 유용했다. 앞쪽 파라미터는 고정하고 마지막만 next 함수로 제한하는 식의 타입 안전성을 확보할 수 있었다.

type MiddlewareArgs<T extends any[] = []> = 
  [req: Request, res: Response, ...custom: T, next: NextFunction];

const authMiddleware = (
  ...args: MiddlewareArgs<[token: string]>
) => {
  const [req, res, token, next] = args;
  // 타입 추론 완벽하게 동작
};

가변 인자 함수의 타입 안전성이 크게 개선됐다. 사소해 보이지만 실무에서는 꽤 자주 마주치는 패턴이라 만족스러운 업데이트였다.

TypeScript 4.2의 Leading/Middle Rest Elements in Tuple Types