개발 기록 남기기✍️

[TypeScript] 템플릿 리터럴 타입을 키로 갖는 객체 만들기 본문

Front-End/TypeScript

[TypeScript] 템플릿 리터럴 타입을 키로 갖는 객체 만들기

너해동물원 2024. 4. 18. 14:02

🚨 마주한 문제

 

공통코드를 템플릿 리터럴 타입으로 다음과 같이 정의했어요.

type Integer = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9;

type CODE = `CODE${Integer}${Integer}${Integer}`;

 

그 중 공통코드 일부를 키로 갖는 객체를 만들려고 했어요.

const 예시객체: Record<CODE, string> = {
  CODE001: '테스트',
};

 

그랬더니 다음과 같은 에러가 터집니다.

퍼-엉

 

예시객체는 CODE000 부터 CODE999까지의 키를 모두 보유한 객체가 되어야 한다는 오류입니다.

 

 

1️⃣ 1트

const 예시객체: Partial<Record<CODE, string>> = {
  CODE001: '테스트',
};

 

Partial로 Record를 감싸주면 타입에러는 발생하지 않지만, value에 undefined가 올 수 있게 되어버립니다.

이것은 우리가 의도한 코드가 아니에요. 🥹

문제를 해결하기 위해 분산적인 조건부 타입을 사용해보아요.

 

 

 

 

✨ 분산적인 조건부 타입

제네릭 타입 위에서 조건부 타입은 유니온 타입을 만나면 분산적으로 동작합니다.

type IsStringType<T> = T extends string ? 'yes' : 'no';

type T1 = IsStringType<string | number>;
// type T1 = string extends string ? 'yes' : 'no' | number extends string ? 'yes' : 'no';
// type T1 = 'yes' | 'no';

한마디로 유니온으로 묶인 타입 하나하나마다 조건부 타입 검사를 하고 그 결과값들을 묶어 다시 유니온으로 반환하는 것입니다.

주의해야 할 점은, 분산 조건부 타입은 naked type parameter가 있을 때만 동작합니다.

 

naked type parameter : 제네릭 T와 같이 의미가 없는 타입 파라미터를 말하며, 만일 직접 리터럴 타입을 명시하거나 T[]와 같이 변환된 타입 파라미터이면 naked가 아니게 된다.

type T2 = string | number extends string ? 'yes' : 'no';
// type T2 = 'no'

 

따라서 T2의 경우에는 string | number가 string의 서브셋이 아닌 슈퍼셋이기 때문에 false가 되어 no가 됩니다.

그리고 분산 조건부 타입에서 never 타입으로 분산이 되었을 경우, 해당 타입은 유니온에서 제외시킵니다.

 

type Never<T> = T extends number ? T : never;

type Types = number | string | object;

type ExcludedType = Never<Types>;
// type ExcludedType = number

 

분산 조건부 타입에서의 never는 제외를 의미합니다. 따라서 제네릭으로 들어온 타입 중, false를 반환하는 타입은 아예 없애버립니다.

이제, 분산 조건부 타입과 never 타입을 조합해서 우리가 원하는 공통코드 객체의 타입을 만들어봅시다!

 

 

✨ 해결

export type CommonCodeRecord<T> = T extends string ? Record<T, string> : never;

const 예시객체: CommonCodeRecord<CODE> = {
  CODE001: '테스트',
};

key 값으로 사용할 공통코드의 타입이 string의 서브셋이면 해당 값을 key로 가진 객체를 반환하도록 처리했습니다.

이제 CommonCodeRecord의 타입은 Record<'CODE000', string> 부터 Record<'CODE999', string>까지의 유니온을 가지는 타입이 되어 예시객체의 key에 공통코드 타입만 할당할 수 있음과 동시에 모든 공통코드를 다 입력하지 않아도 타입 에러가 발생하지 않습니다.

 

이제 원하는 대로 타입이 동작하게 되었어요 야호~