TypeScript 5.4의 NoInfer 유틸리티 타입 실전 적용기
문제 상황
프로젝트에서 기본 설정과 사용자 설정을 병합하는 유틸리티 함수를 작성하던 중 타입 추론 문제가 발생했다.
function mergeConfig<T>(defaults: T, overrides: Partial<T>): T {
return { ...defaults, ...overrides };
}
const defaults = { theme: 'light', timeout: 3000 };
const result = mergeConfig(defaults, { theme: 'dark', extra: 'value' });
// extra가 타입 에러를 발생시키지 않음
overrides가 defaults에 없는 키를 받아들이는 문제였다. TypeScript가 두 인자 모두에서 타입을 추론하면서 발생한 현상이었다.
NoInfer 적용
TypeScript 5.4에서 추가된 NoInfer<T>를 사용해 해결했다.
function mergeConfig<T>(
defaults: T,
overrides: Partial<NoInfer<T>>
): T {
return { ...defaults, ...overrides };
}
const result = mergeConfig(defaults, { theme: 'dark', extra: 'value' });
// Error: Object literal may only specify known properties
NoInfer는 해당 위치에서 타입 추론을 수행하지 않도록 명시한다. overrides는 이제 첫 번째 인자에서만 추론된 타입을 따르게 된다.
다른 활용 사례
API 클라이언트의 헤더 병합 함수에도 적용했다.
class ApiClient<THeaders extends Record<string, string>> {
constructor(private defaultHeaders: THeaders) {}
request(url: string, headers?: Partial<NoInfer<THeaders>>) {
const finalHeaders = { ...this.defaultHeaders, ...headers };
// fetch 로직
}
}
const client = new ApiClient({ 'X-API-Key': 'secret' });
client.request('/api', { 'X-Custom': 'value' }); // Error
기존에는 Omit이나 복잡한 조건부 타입으로 우회했는데, NoInfer로 간결하게 해결됐다. 제네릭 함수 설계 시 타입 추론 방향을 명확히 제어할 수 있어서 유용했다.