-
[TS] 유틸리티 타입Front-end 개발 2024. 2. 29. 09:03
11.1 유틸리티 타입 개념
유틸리티 타입(Utiltiy Types)이란 일반적인 타입 변환을 쉽게 할 수 있도록 타입스크립트에서 전역으로 제공하는 유용한 타입들을 지칭한다. 예를 들면, 추가적인 타입 정의 없이 모든 속성을 선택적 또는 읽기 전용 프로퍼티로 변경한 새로운 타입을 생성하거나, 기존의 정의된 타입을 속성/타입을 선택 또는 제외하여 재사용할 수 있다. 그리고 함수와 클래스의 매개변수의 타입 또한 튜플 형태로 뽑아내는 등의 편리한 기능을 제공한다.
유틸리티 타입은 앞서 우리가 배웠던 타입 문법을 활용해 충분히 구현할 수 있다. 하지만 타입스크립트로 프로젝트를 진행하며 자주 사용되는 타입 변환을 타입 별칭으로 미리 정의되어 있어 편리하게 사용할 수 있다. 유틸리티 타입 구현체는 우리가 npm을 통해 설치한 타입스크립트 lib 폴더의 lib.es5.d.ts 파일에서 찾아볼 수 있다. (node_modules > typescript > lib > lib.es5.dts)
유틸리티 타입을 사용하면 중복된 타입 선언 코드를 피할 수 있고, 유지보수도 더욱 편리 해진다. 또한 원하는 타입을 정확하게 설정하여 컴포넌트, 함수의 안정성과 사용성을 높이고 예상치 못한 런타임 에러를 줄일 수 있다.
유틸리티 타입 설명 (대표 타입) Released/
UpdateAwaited<Type> 비동기 함수의 await 또는 프로미스의 .then 메서드 같이 재귀적으로 프로미스를 풀어내는 작업에서, 프로미스의 결괏값 타입을 새로운 타입으로 반환
(프로미스)4.5 ✅ Partial<Type> 주어진 타입의 모든 속성을
선택적 프로퍼티를 가지는 새로운 타입을 반환 (인터페이스)2.1 ✅ Required<Type> 주어진 타입의 모든 속성을
필수 프로퍼티를 가지는 새로운 타입을 반환 (인터페이스)2.8 ✅ Readonly<Type> 주어진 타입의 모든 속성을
읽기 전용 프로퍼티를 가지는 새로운 타입을 반환 (인터페이스)2.1 ✅ Record<Keys, Type> 일괄 기록할 속성 키로 Keys를, 값으로 Type을 갖는 새로운 타입을 반환
(인터페이스)2.1 ✅ Pick<Type, Keys> 주어진 타입 속성 중에서 Keys에 포함된 속성만 뽑아
새로운 타입을 반환 (인터페이스)2.1 ✅ Omit<Type, Keys> 주어진 타입에서 Keys에 포함된 속성을 제외한 나머지 속성을 가진
새로운 타입을 반환 (인터페이스)3.5 ✅ Exclude<Type, ExcludedUnion> 주어진 타입에서 ExcludedUnion를 제외한 새로운 타입 반환
(유니언)2.8 ✅ Extract<Type, Union> 주어진 타입에서 Union을 추출한 새로운 타입 반환
(유니언)2.8 NonNullable<Type> 주어진 타입에서 null과 undefind를 제외한 새로운 타입 반환
(유니언)2.8 Parameters<Type> 주어진 함수의 매개변수 타입을 새로운 튜플 타입으로 반환
(함수, 튜플)3.1 ConstructorParameters<Type> 주어진 클래스의 매개변수 타입을 새로운 튜플 타입으로 반환
(클래스, 튜플)3.1 ✅ ReturnType<Type> 주어진 함수의 반환(return) 타입을 새로운 타입으로 반환 (함수) 2.8 InstanceType<Type> 주어진 클래스의 인스턴스 타입을 새로운 타입으로 반환 (클래스) 2.8 ThisParameterType<Type> 주어진 함수의 명시적 this 매개변수 타입을 새로운 타입으로 반환 (함수) 3.3 OmitThisParameter<Type> 주어진 함수의 명시적 this 매개변수 타입을 제거한 새로운 타입 반환 (함수) 3.3 ThisType<Type> 주어진 타입의 this 컨텍스트를 명시하고 별도의 타입 반환 없음 (인터페이스) 2.3 Uppercase<StringType> 문자열의 각 문자를 대문자로 변환 (String) 4.1 Lowercase<StringType> 문자열의 각 문자를 소문자로 변환 (String) 4.1 Capitalize<StringType> 문자열의 첫 번째 문자를 대문자로 변환 (String) 4.1 Uncapitalize<StringType> 문자열의 첫 번째 문자를 소문자로 변환 (String) 4.1 타입스크립트 공식 문서에서 소개하는 총 21 가지의 유틸리티 타입이다. 다양한 유틸리티 타입 중에서 자주 사용되는 유틸리티 타입(✅)을 중점으로 알아보자.
💡 내장 문자열 조작 타입(Intrinsic String Manipulation Types)
내장 문자열 조작 타입인 Uppercase, Lowercase, Capitalize, Uncapitalize 타입은 이름 그대로 문자열 조작을 위해 만들어진 타입이며, 성능을 위해 컴파일러에 기본 내장되어 있어 다른 유틸리티 타입들과 달리 구현체를 타입스크립트 라이브러리 .d.ts 파일에서는 찾을 수 없다.
내장 문자열 조작 탕비의 구현체는 아래 코드와 같다. 이 내장 타입들은 타입스크립트 4.1 이후로 자바스크립트 문자열 런타임 함수를 직접 사용함으로써 언어, 지역 설정, 출력 형식, 통화 형식 등의 로케일(locale) 설정을 고려하지 않고 문자열을 처리한다.function applyStringMapping(symbol: Symbol, str: string) { switch (intrinsicTypeKinds.get(symbol.escapedName as string)) { case IntrinsicTypeKind.Uppercase: return str.toUpperCase(); case IntrinsicTypeKind.Lowercase: return str.toLowerCase(); case IntrinsicTypeKind.Capitalize: return str.charAt(0).toUpperCase() + str.slice(1); case IntrinsicTypeKind.Uncapitalize: return str.charAt(0).toLowerCase() + str.slice(1); } return str; }
11.1.2 커스텀 유틸리티
타입을 정확하게 설정함으로써 컴포넌트 및 함수의 안정성과 사용성을 높일 수 있다. 하지만 타이핑을 하다 보면 표현하기 힘든 타입을 마주쳤을 때, 기존 유틸리티 타입만으로 표현하는 데 한계를 느낄 수 있다. 이런 경우 유틸리티 타입을 활용한 커스텀 유틸리티 타입을 만들어 문제를 해결할 수 있다. 물론 커스텀 유틸리티 타입 함수를 구현하기란 쉬운 일이 아니다. 하지만 어떤 타입을 구현해야 하는지 파악하고, 필요한 타입을 세분화하여 단계적으로 구현해나가다 보면 원하는 타입을 표현할 수 있을 것이다.
아래 예시는 여러 속성 중 하나의 프로퍼티만 받는 PickOne 타입이다. 사용자가 서비스 상에서 account 또는 card 중 오직 하나의 방식으로만 결제를 진행하도록 하는 로직을 위해 작성한 타입이다. { account: string } | { card: string }으로 구현하면 account와 card 프로퍼티를 모두 가진 객체도 허용되어 원하는 기능을 구현할 수 없다. 최종 결제 방식이 선택되면 다른 하나의 방식은 존재하더라도 undefined로 바꿔주어야 한다.
// 출처: 우아한 타입스크립트 with 리액트 p.168 type PickOne<T> = { [P in keyof T]: Record<P, T[P]> & Partial<Record<Exclude<keyof T, P>, undefined>>; }[keyof T];
11.2 자주 쓰는 Utility Types
대부분의 유틸리티 타입의 구현부는 node_modules > typescript > lib > lib.es5.d.ts에 존재한다.
구현부를 보면 해당 유틸리티 타입에 대해 이해가 더 쉬워진다.
11.2.1 Partial<Type>
- 특징: 주어진 타입의 모든 속성이 선택적 프로퍼티로 설정된 새로운 타입을 반환한다.
- 타입 변수의 대표적인 타입: interface
- Partial 타입의 구현부
// Make all properties in T optional type Partial<T> = { [P in keyof T]?: T[P]; };
Partial 타입의 예시
interface IUser { name: string, age: number } const userA: IUser = { // Error: Property 'age' is missing name: 'A' }; const userB: Partial<IUser> = { name: 'B' };
Partial<IUser>는 다음과 같이 이해할 수 있다.
interface INewType { name?: string, age?: number }
11.2.2 Required<Type>
- 특징: 주어진 타입의 모든 속성이 필수 프로퍼티를 가지는 새로운 타입을 반환한다.
- 타입 변수의 대표적인 타입: Interface
- Required 타입의 구현부
// Make all properties in T required type Required<T> = { [P in keyof T]-?: T[P]; };
Required 타입의 예시
interface IEmployee { name?: string, age?: number }; const employee1: IEmployee = { name: 'Henry' }; // const employee2: Required<IEmployee> = { // Error: Property 'age' is missing // name: 'Lisa' // };
Required<IEmployee>는 다음과 같이 이해할 수 있다.
interface IRequiredProperty { name: string, age: number };
11.2.3 Readonly<Type>
- 특징: 주어진 타입의 모든 속성을 읽기 전용 속성을 가지는 새로운 타입을 반환한다.
- 타입 변수의 대표적인 타입: Interface
- Readonly 타입의 구현부
//Make all properties in T readonly type Readonly<T> = { readonly [P in keyof T]: T[P]; };
Readonly 타입의 예시
interface IDepartment { id: number, name: string }; const department1: IDepartment = { id: 10, name: 'Development' }; department1.name = 'Accounting' const department2: Readonly<IDepartment> = { id: 200, name: 'legal' }; // department2.name = 'Marketing' // Error: read-only property
Readonly<IDepartment>는 다음과 같이 이해할 수 있다.
interface IReadonlyProperty { readonly id: number, readonly name: string }
11.2.4 Pick<Type, Keys>
- 특징: 주어진 타입 프로퍼티 중에서 Keys에 포함된 프로퍼티만 뽑아 새로운 타입을 반환한다.
- 타입 변수의 대표적인 타입: Interface
- Pick 타입의 구현부
// From T, pick a set of properties whose keys are in the union K type Pick<T, K extends keyof T> = { [P in K]: T[P]; };
Pick 타입의 예시
interface IAnimal { name: string, age: number, breed: string, vaccination: boolean } type TKeyPick = 'name' | 'vaccination'; const animal: Pick<IAnimal, TKeyPick> = { name: 'Leo', vaccination: true, // breed: 'retriever' // Error }
Pick<IAnimal, TKeyPick>은 다음과 같은 의미를 가지게 된다.
interface IPick { name: string, vaccination: boolean }
11.2.5 Omit<Type, Keys>
- 특징: 주어진 타입 속성 중에서 Keys에 포함된 프로퍼티를 제외한 나머지 프로퍼티를 가진 새로운 타입을 반환한다.
- 타입 변수의 대표적인 타입: Interface
- Omit 타입의 구현부
// Construct a type with the properties of T except for those in type K. type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;
Omit 타입의 예시
interface IFriend { name: string, age: number, email: string, hasCar: boolean } type TKeyOmit = 'email' | 'hasCar'; const friend: Omit<IFriend, TKeyOmit> = { name: 'Kim', age: 27, // haCar: true // Error: not assignable }
Omit<IFriend, TKeyOmit>은 다음과 같은 의미를 가진다.
interface IOmit { name: string, age: number, // email: string, // hasCar: boolean }
11.2.6 Record<Keys, Type>
- 특징: 일괄 기록할 속성 키로 Keys를, 값으로 Type을 갖는 새로운 타입을 반환한다.
- 타입 변수의 대표적인 타입: Interface
- Record 타입의 구현부
// Construct a type with a set of properties K of type T type Record<K extends keyof any, T> = { [P in K]: T; };
Record 타입의 예시
type TUser = 'john' | 'michael'; const developments: Record<TUser, number> = { john: 12, michael: 13 };
Record<TUser, number>는 다음과 같은 의미를 가진다.
interface IRecord { john: number, lewis: number }
11.2.7 NonNullable<Type>
- 특징: 주어진 타입에서 null과 undefined를 제외한 새로운 타입을 반환한다.
- 타입 변수의 대표적인 타입: Union
- NonNullable 타입의 구현부
// Exclude null and undefined from T type NonNullable<T> = T & {};
NonNullable 타입의 예시
type TUnion3 = boolean | string | number | undefined; const test1: TUnion3 = undefined; // const test2: NonNullable<TUnion3> = null; // Error: not assignable // const test3: NonNullable<TUnion3> = undefined; // Error: not assignable
NonNullable<TUnion3>의 타입은 아래와 같은 의미를 가진다.
type TNonNullable = boolean | string | number;
11.2.8 Exclude<Type, ExcludedUnion>
- 특징: 주어진 타입에서 ExcludedUnion를 제외한 새로운 타입을 반환한다.
- 타입 변수의 대표적인 타입: Union
- Exclude 타입의 구현부
// Exclude from T those types that are assignable to U type Exclude<T, U> = T extends U ? never : T;
Exclude 타입의 예시
type TUnion = boolean | string | number; const type1: Exclude<TUnion, boolean> = 'string | number'; const type2: Exclude<TUnion, boolean> = 100; // const type3: Exclude<TExclude, boolean = false; // Error: not assignable
Exclude<TUnion, boolean>은 다음과 같은 의미를 가진다.
type TExclude = string | number;
11.2.9 Extract<Type, Union>
- 특징: 주어진 타입에서 Union을 추출한 새로운 타입을 반환한다.
- 타입 변수의 대표적인 타입: Union
- Extract 타입의 구현부
// Extract from T those types that are assignable to U type Extract<T, U> = T extends U ? T : never;
Extract 타입의 예시
type TUnion1 = number | boolean; type TUnion2 = string | number; const out1: Extract<TUnion1, TUnion2> = 100; // const out2: Extract<TUnion1, TUnion2> = false; // Error: not assignable
Extract<TUnion1, TUnion2>는 다음과 같은 의미를 가진다.
type TExtract = number;
참고자료1. 타입스크립트 공식 문서
참고자료2. 한눈에 보는 타입스크립트
'Front-end 개발' 카테고리의 다른 글
[React] 훅! useEffect (0) 2024.03.14 [Copilot] JS에서 함수는 세미콜론(;)을 붙이지 않는 이유 (0) 2024.03.13 [Bug Fix] AWS S3 ACL Error: The bucket does not allow ACLs (0) 2024.02.24 [TS] Promise에서 Generic과 제네릭의 제약 조건 (0) 2024.02.16 [멋쟁이빌더] 사전 설명회 (0) 2024.02.07