TypeScript(타입스크립트) 제네릭

NSH·2022년 6월 8일
0

TypeScript

목록 보기
7/8

재사용 가능한 컴포넌트를 구축하는 것은 소프트웨어 엔지니어링에서 중요한 부분이다. 현재의 데이터와 미래의 데이터를 모두 다룰 수 있는 컴포넌트는 소프트웨어 시스템을 구성하는데 있어서 가장 유연한 능력을 제공할 것이다.

1. 제네릭

아래의 예제를 매개변수를 그대로 반환하는 함수이다.

function numberReturnFunc(arg: number): number {
	return arg;
}
function stringReturnFunc(arg: string): string {
	return arg;
}
function booleanReturnFunc(arg: boolean): boolean {
	return arg;
}

함수의 기능은 같지만 반환하는 타입이 다르기 때문에 함수를 여러개로 구현했다. 이런 방식은 제네릭을 사용해서 하나의 함수로 구현이 가능하다.

function genericReturnFunc<T>(arg: T): T {
	return arg;
}

제네릭 함수 구현 방법은 함수명 뒤에 <T>를 추가하고, T를 매개변수의 타입과 반환 타입으로 설정할 수 있다. (Type 의 약자로 T를 많이 사용한다.)

2. 제네릭 함수 호출

제네릭 함수를 호출하는 방법이다. <>안에 인수의 타입을 작성하여 호출하며 인수와 인수의 타입이 다르면 경고문이 발생한다.

// 정상
let num = genericReturnFunc<number>(123);
let str = genericReturnFunc<string>('abc');

// 오류: Argument of type 'string' is not assignable to parameter of type 'number'
let str = genericReturnFunc<number>('abc');

3. any 타입의 문제점

3.1 반환 타입을 알 수 없다.

any 타입으로 매개변수의 타입과 함수의 반환 타입을 지정할 수 있다. any 타입은 어떤 타입이든 받을 수 있다는 점에서 제네릭 이지만, 실제 함수가 반환할 때 어떤 타입인지 알 수 없다.

function anyReturnFunc(arg: any): any {
	return arg;
}

// number 타입인 123을 전달했지만, 어떤 타입을 return 하는지 알 수 없다.
let num = anyReturnFunc(123);
// string 타입인 123을 전달했지만, 어떤 타입을 return 하는지 알 수 없다.
let str = anyReturnFunc('abc');

따라서 제네릭 함수를 사용해서 어떤 타입을 반환하는 알 수 있게 작성하는데 유지보수 측면에서 상당히 좋다.

function genericReturnFunc<T>(arg: T): T {
	return arg;
}

let num = genericReturnFunc<number>(123);

제네릭 함수를 호출할 때 타입 인수 추론을 사용하는 방법이 존재한다. 전달하는 인수를 컴파일러가 추론해서 T 값을 자동으로 정하게 된다. 하지만 코드를 간결하게 해주는 장점이 있지만, 타입을 유추하기 어려운 경우에는 <> 안에 타입을 명시하는 것이 좋다.

let str = genericReturnFunc('abc');

3.2 매개변수의 프로퍼티를 체크하지 않는다.

매개변수의 length를 반환하는 코드이다.

any 타입은 매개변수의 프로퍼티를 체크하지 않아 오류가 발생하지 않는다.

// any
function anyReturnFunc(arg: any): any {
	return arg.length;
}

제네릭 함수는 어떤 타입이 올지 모르기 때문에 length 프로퍼티를 사용할 수 없다.

// generic
function genericReturnFunc<T>(arg: T): T {
	return arg.length
}

4. 제네릭 클래스

제네릭 클래는 클래스 이름 뒤 <> 안에 타입을 작성한다.

class GenericClass<T> {
	value: T;
  	add: (x: T, y: T) => T;
}

let num = new GenericClass<number>();
num.value = 0;
num.add = function(x, y) { return x + y }

GenericClassnumber 타입 이외의 타입도 사용할 수 있다.

let num = new GenericClass<string>();
num.value = '';
num.add = function(x, y) { return x + y }

5. 제네릭 제약 조건

제약 조건을 명시하는 인터페이스를 만들고 제약 사항을 extends 키워드로 연결한다.

interface Lengthwise {
	length: number;
}

function loggingIdentity<T extends Lengthwise>(arg: T): T {
    // length 프로퍼티가 있다는 것을 알기 때문에 오류가 발생하지 않는다.
    console.log(arg.length);  
    return arg;
}

제네릭 함수는 제한되어 있기 때문에 모든 타입에 동작하지 않고 length 프로퍼티가 있는 값을 전달해야 한다.

// string은 length 프로퍼티가 존재하므로 에러가 발생하지 않는다.
loggingIdentity('abc');
// number는 length 프로퍼티가 존재하지 않아 에러가 발생한다.
loggingIdentity(123);
profile
잘 하고 싶다.

0개의 댓글