Recursive Types(재귀 타입)
Recursive Types(재귀 타입)는 자기 참조하는 타입을 정의할 수 있도록 해준다다. 이는 트리, 그래프 등과 같이 반복적인 형태의 데이터 구조를 다룰 때 유용하다.
예를 들어, 다음과 같은 트리 구조를 타입으로 표현할 수 있다.
type TreeNode = {
value: number;
left?: TreeNode;
right?: TreeNode;
}
위 타입 정의에서 TreeNode은 left와 right 프로퍼티가 자기 자신을 참조하는 타입이다. 이는 트리가 자식 노드를 가질 수 있기 때문에 가능하다.
Recursive Types은 무한히 중첩될 수 있기 때문에, 끝없이 반복되는 형태의 데이터 구조를 다루기에 적합하다.
하지만 이를 사용할 때에는 주의해야 할 점이 있다. 타입스크립트에서는 재귀적으로 정의된 타입이 무한히 반복되는 것을 막기 위해 타입 깊이(depth)를 제한하고 있다. 기본적으로는 50단계로 제한되어 있으며, 이를 초과할 경우 타입스크립트 컴파일러가 에러를 발생시킵니다.
Recursive Types은 인터페이스나 제네릭 타입에서도 사용할 수 있다. 아래는 인터페이스에서 Recursive Types을 사용한 예시이다.
interface LinkedListNode<T> {
value: T;
next?: LinkedListNode<T>;
}
type IntLinkedListNode = LinkedListNode<number>;
위 예제에서는 LinkedListNode 인터페이스가 next 프로퍼티가 자기 자신을 참조하는 Recursive Types이다. 이를 기반으로 IntLinkedListNode 타입을 정의할 수 있다.
Recursive Types을 사용할 때는 재귀적으로 정의되는 타입이 올바르게 동작하도록 신중하게 설계해야 한다. 하지만 이를 사용함으로써 특정 형태의 데이터 구조를 타입으로 표현할 수 있어서 유용하다.
Tuple Types(튜플 타입)
튜플 타입(Tuple Types)은 고정된 개수의 요소(element)와 각 요소의 타입(type)이 미리 정의된 배열(array) 타입이다. 다른 배열 타입과는 달리, Tuple Types은 각 요소마다 다른 타입을 지정할 수 있다.
Tuple Types은 다음과 같이 정의할 수 있다.
let myTuple: [string, number] = ["hello", 123];
위 예시에서, myTuple은 문자열(string)과 숫자(number) 두 요소로 이루어진 튜플 타입이다. 요소의 타입은 순서대로 배열 안에 지정된다. 이 경우, 첫 번째 요소는 문자열(string) 타입이며, 두 번째 요소는 숫자(number) 타입이다.
Tuple Types은 배열과 유사하게 인덱스(index)를 통해 각 요소에 접근할 수 있다. 이 때, 요소의 타입에 맞는 메소드나 속성을 사용할 수 있다.
console.log(myTuple[0].toUpperCase()); // "HELLO"
console.log(myTuple[1].toFixed(2)); // "123.00"
Tuple Types은 고정된 개수의 요소와 각 요소의 타입이 미리 정의되어 있기 때문에, 배열 타입보다 더 구체적인 타입을 지정할 수 있다.
Array과 다른 Tuple Types의 특징
- 요소의 개수와 타입이 고정되어 있다.
Tuple Types은 요소의 개수와 타입이 미리 정해져 있기 때문에, 배열과는 다르게 요소의 개수나 타입을 동적으로 변경할 수 없다. 예를 들어, [string, number] 타입의 Tuple은 반드시 첫 번째 요소가 문자열이고 두 번째 요소가 숫자여야 한다.
- 인덱스에 따라 요소의 타입이 결정된다.
Tuple Types에서는 배열과 마찬가지로 인덱스를 사용하여 각 요소에 접근할 수 있다. 이 때, 각 인덱스에 따라 요소의 타입이 결정된다. 예를 들어, [string, number] 타입의 Tuple에서 첫 번째 요소는 문자열이므로, myTuple[0]의 타입은 string이 된다.
- 배열 타입보다 더 구체적인 타입을 지정할 수 있다.
Tuple Types은 요소의 개수와 타입이 미리 정해져 있기 때문에, 배열 타입보다 더 구체적인 타입을 지정할 수 있다. 예를 들어, string[] 타입은 문자열만 포함하는 배열을 의미하지만, [string, number] 타입의 Tuple은 문자열과 숫자를 각각 포함하는 배열을 의미한다.
- 유틸리티 타입을 사용하여 Tuple Types을 조작할 수 있다.
타입스크립트는 유틸리티 타입(utility type)을 제공하여 Tuple Types을 조작할 수 있다. 예를 들어, readonly 유틸리티 타입을 사용하면 Tuple Types의 요소를 읽기 전용으로 만들 수 있다.
let myTuple: readonly [string, number] = ["hello", 123];
myTuple[0] = "world"; // Error: Index signature in type 'readonly [string, number]' only permits reading.
위 예시에서, myTuple을 readonly [string, number] 타입으로 정의하면, 요소를 수정하는 것이 불가능해진다.
Tuple Types을 사용한 다양한 상황
- 함수에서 여러 값을 반환할 때
일반적으로 함수에서는 하나의 값만 반환한다. 하지만, 때로는 함수에서 여러 값을 반환해야 하는 상황이 있을 수 있다.
이 때, Tuple Types을 사용하면 여러 값을 한 번에 반환할 수 있다.
function getPerson(): [string, number] {
return ["Alice", 30];
}
const [name, age] = getPerson();
console.log(name); // "Alice"
console.log(age); // 30
위 예시에서, getPerson 함수는 이름과 나이를 문자열과 숫자의 Tuple Types으로 반환한다. 반환된 Tuple Types을 구조분해할당을 사용하여 각 변수에 할당한다.
- 고정된 개수의 요소를 포함하는 컬렉션
일반적으로 배열은 동적으로 크기가 조정될 수 있는 컬렉션이다. 하지만, 경우에 따라서는 고정된 개수의 요소를 포함하는 컬렉션이 필요할 수 있다. 이 때, Tuple Types을 사용하면 각 요소의 타입이 미리 정해져 있으므로 타입 안정성(type safety)이 높아진다.
type Point = [number, number];
function getDistance(point1: Point, point2: Point): number {
const dx = point2[0] - point1[0];
const dy = point2[1] - point1[1];
return Math.sqrt(dx ** 2 + dy ** 2);
}
const p1: Point = [0, 0];
const p2: Point = [3, 4];
console.log(getDistance(p1, p2)); // 5
위 예시에서, Point는 두 개의 숫자를 요소로 갖는 Tuple Types이다. getDistance 함수는 두 개의 Point 타입의 인수를 받아 두 점 사이의 거리를 계산한다.
Tuple Types은 고정된 개수의 요소와 각 요소의 타입이 미리 정의되어 있기 때문에, 코드의 가독성과 유지보수성을 높이는데 도움을 준다. 하지만, Tuple Types은 동적으로 요소를 추가하거나 제거하는 등의 작업이 어려운 점이 있으므로, 사용 시 주의해야 한다.
Utility Types(유틸리티 타입)
Utility Types(유틸리티 타입)은 일반적인 타입 선언을 보다 쉽고 간결하게 작성할 수 있도록 도와주는 타입이다. 유틸리티 타입은 기존 타입을 활용하여 새로운 타입을 만들어내는 방식으로 사용된다.
Partial<Type>
Type의 모든 속성을 optional로 만들어 새로운 타입을 생성한다.
interface User {
name: string;
age: number;
}
type PartialUser = Partial<User>;
// PartialUser: { name?: string; age?: number; }
Required<Type>
Type의 모든 속성을 필수로 만들어 새로운 타입을 생성한다.
interface User {
name?: string;
age?: number;
}
type RequiredUser = Required<User>; // RequiredUser: { name: string; age: number; }
Readonly<Type>
Type의 모든 속성을 읽기 전용으로 만들어 새로운 타입을 생성한다.
interface User {
name: string;
age: number;
}
type ReadonlyUser = Readonly<User>; // ReadonlyUser: { readonly name: string; readonly age: number; }
Record<Keys, Type>
Type에 대한 Key-Value 쌍을 가진 객체를 생성한다.
type User = { name: string; age: number };
type UserRecord = Record<string, User>; // UserRecord: { [key: string]: User; }
Exclude<Type, ExcludedUnion>
Type에서 ExcludedUnion에 속하는 타입을 제외한 새로운 타입을 생성한다.
type Color = 'red' | 'blue' | 'green';
type ExcludedColor = Exclude<Color, 'green'>; // ExcludedColor: "red" | "blue"
Extract<Type, Union>
Union 타입(Type)에서 Type과 일치하는 타입을 추출하여 타입으로 사용한다.
type MyUnion = 'a' | 'b' | 'c';
type MyExtract = Extract<'a' | 'c' | 'd', MyUnion>; // MyExtract: 'a' | 'c'
Pick<Type, Keys>
Type에서 지정한 속성만을 포함하는 새로운 타입을 생성한다.
interface User {
name: string;
age: number;
email: string;
}
type UserBasicInfo = Pick<User, 'name' | 'age'>; // UserBasicInfo: { name: string; age: number; }
Omit<Type, Keys>
Type에서 지정한 속성을 제외한 나머지 속성으로 새로운 타입을 생성한다.
interface User {
name: string;
age: number;
email: string;
}
type UserWithoutEmail = Omit<User, 'email'>; // UserWithoutEmail: { name: string; age: number; }
ReturnType<Type>
함수 타입(Type)의 반환 타입을 추출하여 타입으로 사용한다.
function getUser() {
return { name: 'John', age: 30 };
}
type User = ReturnType<typeof getUser>; // User: { name: string; age: number; }
Parameters<Type>
함수 타입(Type)의 매개변수 타입들을 튜플 형태로 추출하여 타입으로 사용한다.
function getUser(name: string, age: number) {
return { name, age };
}
type UserParams = Parameters<typeof getUser>; // UserParams: [string, number]
RequiredKeys<Type>
Type에서 속성값이 required인 키를 추출하여 타입으로 사용한다.
interface User {
name?: string;
age: number;
email: string;
}
type RequiredUserKeys = RequiredKeys<User>; // RequiredUserKeys: "age" | "email"
PartialKeys<Type>
Type에서 속성값이 optional인 키를 추출하여 타입으로 사용한다.
interface User {
name?: string;
age: number;
email: string;
}
type OptionalUserKeys = PartialKeys<User>; // OptionalUserKeys: "name"
NonNullable<Type>
Type에서 null, undefined가 아닌 타입을 추출하여 타입으로 사용한다.
type User = { name: string | null | undefined; age: number | null | undefined };
type NonNullableUser = NonNullable<User['name'] | User['age']>; // NonNullableUser: string | number
InstanceType<Type>
클래스 타입(Type)에서 인스턴스 타입을 추출하여 타입으로 사용한다.
class User {
name: string;
age: number;
}
type UserInstance = InstanceType<typeof User>; // UserInstance: User
ThisType<Type>
함수에서 this 타입(Type)을 명시적으로 지정하여 타입스크립트 컴파일러가 해당 함수 내에서 this로 사용하는 객체를 타입 체크할 수 있도록 한다.
interface User {
name: string;
greet(this: User): void;
}
function greeting(this: User) {
console.log('Hello, ' + this.name);
}
const user: User = { name: 'John', greet: greeting };
user.greet(); // Hello, John
ThisParameterType<Type>
함수 타입(Type)에서 this 매개변수 타입을 추출하여 타입으로 사용한다.
function getUser(this: { name: string }) {
return this.name;
}
type UserThisParam = ThisParameterType<typeof getUser>; // UserThisParam: { name: string }
OmitThisParameter<Type>
함수 타입(Type)에서 this 매개변수를 제거하여 새로운 함수 타입(Type)을 생성한다.
function getUser(this: { name: string }) {
return this.name;
}
type UserWithoutThis = OmitThisParameter<typeof getUser>; // UserWithoutThis: () => string
UnionToIntersection<Union>
Union 타입(Union)에서 인터섹션 타입(Intersection)을 추출하여 타입으로 사용한다.
type MyUnion = { name: string } | { age: number };
type MyIntersection = UnionToIntersection<MyUnion>; // MyIntersection: { name: string } & { age: number }
First<Type>
Tuple 타입(Type)에서 첫 번째 요소의 타입을 추출하여 타입으로 사용한다.
type MyTuple = [string, number, boolean];
type MyFirst = First<MyTuple>; // MyFirst: string
Last<Type>
Tuple 타입(Type)에서 마지막 요소의 타입을 추출하여 타입으로 사용한다.
type MyTuple = [string, number, boolean];
type MyLast = Last<MyTuple>; // MyLast: boolean
Append<Tuple, Addendum>
Tuple 타입(Tuple)에 다른 타입(Addendum)을 추가하여 새로운 Tuple 타입(Tuple)을 생성한다.
type MyTuple = [string, number];
type MyTupleWithBoolean = Append<MyTuple, boolean>; // MyTupleWithBoolean: [string, number, boolean]
Prepend<Tuple, Addendum>
Tuple 타입(Tuple)의 첫 번째에 다른 타입(Addendum)을 추가하여 새로운 Tuple 타입(Tuple)을 생성한다.
type MyTuple = [number, boolean];
type MyTupleWithBoolean = Prepend<MyTuple, string>; // MyTupleWithBoolean: [string, number, boolean]
OptionalKeys<Type>
객체 타입(Type)에서 속성 이름이 Optional(?)인 속성들의 이름을 문자열 리터럴의 유니온 타입으로 추출한다.
interface MyInterface {
name: string;
age?: number;
gender?: string;
}
type MyOptionalKeys = OptionalKeys<MyInterface>; // MyOptionalKeys: 'age' | 'gender'
ConstructorParameters<Type>
생성자 함수(Type)의 매개변수 타입들을 Tuple 타입으로 추출한다.
class MyClass {
constructor(name: string, age: number) {}
}
type MyConstructorParams = ConstructorParameters<typeof MyClass>; // MyConstructorParams: [string, number]