타입스크립트의 제네릭

타입스크립트의 제네릭

이 기사에서는 타입스크립트에서 제네릭에 대해 설명합니다.

YouTube Video

타입스크립트의 제네릭

타입스크립트의 제네릭은 타입을 매개변수화하여 재사용 가능하고 타입 안전한 함수, 클래스, 인터페이스를 정의할 수 있는 기능입니다. 제네릭을 사용하면 특정 타입에 의존하지 않는 코드를 작성할 수 있으므로 다양한 타입에서 동일한 작업을 수행할 수 있습니다.

제네릭의 기초

제네릭은 타입을 인수로 취하는 템플릿처럼 작동하여 함수와 클래스가 다양한 타입을 다룰 수 있도록 합니다.

제네릭 함수

다음은 제네릭을 사용하여 인수 타입을 지정한 함수의 예입니다.

1function identity<T>(value: T): T {
2    return value;
3}
4
5console.log(identity<number>(42));       // 42
6console.log(identity<string>("Hello"));  // Hello
  • T는 함수 인수 및 반환 값의 타입을 나타내는 제네릭 타입 인수입니다. 실제 타입은 함수가 호출될 때 결정됩니다.
  • <number> 또는 <string>을 명시적으로 지정하여 타입을 정의하는 것입니다.

타입스크립트는 타입 추론을 수행하므로 제네릭 타입은 명시적 지정 없이도 작동합니다.

1function identity<T>(value: T): T {
2    return value;
3}
4
5console.log(identity(42));       // 42
6console.log(identity("Hello"));  // Hello

제네릭의 제한 조건

제네릭에 제한 조건을 두면 특정 타입만 허용하도록 제한할 수 있습니다.

1function loggingIdentity<T extends { length: number }>(arg: T): T {
2    console.log(arg.length);
3    return arg;
4}
5
6loggingIdentity("Hello");  // 5
7loggingIdentity([1, 2, 3]);  // 3
8
9// loggingIdentity(42);  // Error: number does not have a length property.
  • T extends { length: number }를 지정하면 T는 반드시 length 속성을 가진 타입이어야 함을 의미합니다. 따라서 length 속성이 없는 타입은 허용되지 않습니다.

제네릭 클래스

클래스도 제네릭을 사용하여 정의할 수 있습니다. 제네릭 클래스는 프로퍼티와 메서드에 유연한 타입을 제공합니다.

 1class Box<T> {
 2    private _value: T;
 3
 4    constructor(value: T) {
 5        this._value = value;
 6    }
 7
 8    public getValue(): T {
 9        return this._value;
10    }
11
12    public setValue(value: T): void {
13        this._value = value;
14    }
15}
16
17const numberBox = new Box<number>(100);
18console.log(numberBox.getValue());  // 100
19
20const stringBox = new Box<string>("Hello");
21console.log(stringBox.getValue());  // Hello
  • Box<T>는 클래스 내에서 사용되는 타입 T를 제네릭으로 선언합니다. 이를 통해 동일한 클래스를 다양한 타입에 재사용할 수 있습니다.

제네릭 인터페이스

제네릭은 인터페이스에서도 사용할 수 있습니다.

 1interface Pair<T, U> {
 2    first: T;
 3    second: U;
 4}
 5
 6const numberStringPair: Pair<number, string> = { first: 1, second: "One" };
 7console.log(numberStringPair);  // { first: 1, second: 'One' }
 8
 9const booleanArrayPair: Pair<boolean, number[]> = { first: true, second: [1, 2, 3] };
10console.log(booleanArrayPair);  // { first: true, second: [ 1, 2, 3 ] }
  • Pair<T, U>로 두 가지 제네릭 타입을 지정하면 서로 다른 타입의 조합으로 객체를 정의할 수 있습니다.

기본 타입 인수

제네릭 타입 인수에 대해 기본 타입을 지정하는 것도 가능합니다.

1function createArray<T = string>(length: number, value: T): T[] {
2    return Array(length).fill(value);
3}
4
5console.log(createArray(3, "a"));   // ['a', 'a', 'a']
6console.log(createArray(3, 100));   // [100, 100, 100]
  • <T = string>을 사용해 기본 타입 인수를 string으로 설정하고 있습니다. 명시적으로 타입을 지정하지 않으면 Tstring 타입이 됩니다.

제네릭 타입 별칭

제네릭은 타입 별칭(type)으로도 사용할 수 있습니다.

 1type Result<T> = {
 2    success: boolean;
 3    data: T;
 4};
 5
 6const successResult: Result<number> = { success: true, data: 42 };
 7const errorResult: Result<string> = { success: false, data: "Error occurred" };
 8
 9console.log(successResult);  // { success: true, data: 42 }
10console.log(errorResult);    // { success: false, data: 'Error occurred' }
  • Result<T>T 타입의 데이터를 포함하는 결과 객체를 나타냅니다. 이와 같은 방식으로, 제네릭을 사용하여 유연한 타입 별칭을 만들 수 있습니다.

다중 제네릭 타입

다중 제네릭 타입을 사용하면, 더욱 다양한 기능을 가진 함수와 클래스를 정의할 수 있습니다.

1function merge<T, U>(obj1: T, obj2: U): T & U {
2    return { ...obj1, ...obj2 };
3}
4
5const person = { name: "Alice" };
6const job = { title: "Engineer" };
7
8const merged = merge(person, job);
9console.log(merged);  // { name: 'Alice', title: 'Engineer' }
  • merge 함수는 두 가지 다른 타입 TU를 받아 이를 결합하여 새로운 객체를 반환합니다.

요약

  • 제네릭은 타입을 매개변수로 처리하여 재사용 가능하고 타입 안전한 코드를 작성할 수 있도록 해줍니다.
  • 함수, 클래스, 인터페이스에서 제네릭을 사용하면 다양한 타입을 다룰 수 있는 유연한 로직을 작성할 수 있습니다.
  • 타입 인수에 제약을 추가하거나 기본 타입 인수를 설정하여 제네릭의 범위를 제어할 수 있습니다.

제네릭을 사용함으로써 타입에 의존하지 않는 범용 코드를 작성할 수 있으며, TypeScript의 강력한 타입 시스템을 최대한 활용할 수 있습니다.

위의 기사를 보면서 Visual Studio Code를 사용해 우리 유튜브 채널에서 함께 따라할 수 있습니다. 유튜브 채널도 확인해 주세요.

YouTube Video