2024. 12. 23. 11:30ㆍProgramming Language/Node.js
제네릭이란?
타입스크립트에서 타입을 변수처럼 다룰 수 있는 기능으로 특정 타입에 의존하지 않는 함수, 클래스, 인터페이스를 작성할 수 있게 만든다.
제네릭 - 함수
제네릭 함수의 문법
제네릭 함수는 함수의 이름 뒤에 <T> 를 사용해서 만들어주고 내부에 T는 고정된 타입이 아니라 호출 시점에 구체적인 타입으로 대체된다.
function 함수명<T>(매개변수: T): T {
// 함수 로직
return 매개변수;
}
예시를 보자면
이렇게 선언하면
이렇게 함수를 사용할때 함수명과 전달인자 사이에 <타입>을 넣어주면된다.
그러면 저렇게 호출하는 시점에 T를 입력한 타입으로 대체해서 사용된다.
그런데 타입스크립트는 대부분의 제네릭 타입을 자동으로 추론하기 때문에 궂이 명시적으로 타입을 제공하지 않더라도
에러를 발생 시키지 않는다
그리고 제네릭 함수는 여러개의 타입 변수를 사용하는 것도 가능하다.
이때는 그냥
function 함수명<T, U>(매개변수: T, 매개변수2: U): T {
// 함수 로직
return 매개변수; //T 타입의 매개변수
}
와 같이 <>사이에 추가적으로 타입변수를 추가하고 매개변수에도 동일하게 추가로 설정하면 사용이 가능하다.
제네릭 - 클래스
제네릭 클래스의 기본 문법
제네릭 클래스를 정의하기 위해선 클래스 이름 옆에 <T>와 같은 제네릭 타입 매개변수를 추가해줘야한다.
이렇게 추가된 제네릭 타입 매개변수는 클래스의 속성과 메서드 등의 타입으로 이용된다.
아래는 기본적인 제네릭 클래스의 선언 방식이다
class GenericClass<T> {
private value: T;
constructor(value: T) {
this.value = value;
}
getValue(): T {
return this.value;
}
setValue(newValue: T): void {
this.value = newValue;
}
}
클래스를 선언할때 입력한 제네릭 타입 매개변수는 내부의 속성이나 매개변수로 사용되는 것을 볼 수 있다.
이렇게 생성한 클래스를 사용하는 방법은
const instClass = new GenericClass<string>("TEST");
와 같이 클래스를 생성하면
console.log(instClass.getValue());
instClass.setValue("NEW TEST");
console.log(instClass.getValue());
이렇게 사용했을때
이렇게 동작하는 것을 확인할 수 있다.
그리고 이 제네릭 클래스는 함수와 동일하게 다수의 제네릭 타입을 사용할 수도 있다.
class Pair<T, U> {
private first: T;
private second: U;
constructor(first: T, second: U) {
this.first = first;
this.second = second;
}
getFirst(): T {
return this.first;
}
getSecond(): U {
return this.second;
}
}
const pair = new Pair<number, string>(1, "one");
console.log(pair.getFirst()); // 1
console.log(pair.getSecond()); // "one"
제네릭 - 인터페이스
제네릭 인터페이스의 기본 문법
제네릭 인터페이스도 동일하게 인터페이스명 뒤에 <T>를 작성해서 정의한다.
interface GenericInterface<T> {
value: T;
getValue(): T;
}
인터페이스에서 제네릭 타입의 사용은 기본적으로
interface Transformer<T, U> {
(input: T): U;
}
const stringToNumber: Transformer<string, number> = (input) => parseInt(input);
const numberToString: Transformer<number, string> = (input) => input.toString();
console.log(stringToNumber("123")); // 123
console.log(numberToString(456)); // "456"
와 같이 사용한다.
제네릭의 제약조건 추가
제네릭에는 제약조건이란 것을 추가할 수 있는데 이는 제네릭 타입 T가 특정조건을 만족해야만 사용할 수 있도록 제약을 하는 것을 의미한다.
function example<T>(value: T): void {
console.log(value);
}
example(123); // OK
example("Hello"); // OK
example([1, 2]); // OK
기본적으로 제네릭은 위와 같이 모든 타입을 허용한다.
여기에 우리는 제약조건을 걸 수 있는데 T라는 타입이 특정 인터페이스 혹은 클래스 또는 타입의 서브타입이여야 한다는 제약조건을 추가할 수 있다.
이때 제약조건을 추가할 때는 extends키워드를 사용한다.
예를 들어
interface HasLength {
length: number;
}
라는 인터페이스를 생성한 다음에
function logLength<T extends HasLength>(value: T): void {
console.log(value.length);
}
이렇게 제네릭 타입에 해당 인터페이스를 상속해주면 T라는 타입은 length라는 속성이 존재하는 타입만을 허가 하도록 설정된다.
그래서
logLength("Hello"); // 문자열은 length 속성을 가짐
logLength([1, 2, 3]); // 배열도 length 속성을 가짐
logLength({ length: 10, name: "Alice" }); // 객체도 length 속성이 있으면 가능
이렇게 length라는 속성을 갖고 있는 타입의 데이터는 허용하지만
logLength(123); // number에는 length 속성이 없음
logLength(true); // boolean에는 length 속성이 없음
이렇게 length 속성이 없는 타입의 데이터는 허용하지 않는다.
단순하게 extends 타입 만 한다면
function logLength<T extends string | number>(value: T): void {
console.log(value);
}
이때 T는 string 타입이거나 number타입만을 허용한다.
또한
function logLength<T extends number[]>(value: T): void {
console.log(value);
}
와 같이 사용한다면 T는 number타입을 멤버로 가지는 number배열만을 허용하게 된다.
'Programming Language > Node.js' 카테고리의 다른 글
TypeScript에 대한 간단한 정리 - 13. tsconfig.json의 구성 옵션 (0) | 2024.12.24 |
---|---|
TypeScript에 대한 간단한 정리 - 12. 패키지의 타입 선언 (0) | 2024.12.24 |
TypeScript에 대한 간단한 정리 - 10. 클래스와 접근 제어자 (1) | 2024.12.21 |
TypeScript에 대한 간단한 정리 - 09. 함수 - 오버로딩(Overloading) (0) | 2024.12.20 |
TypeScript에 대한 간단한 정리 - 08. 함수 - 명시적 this 타입 (1) | 2024.12.20 |