1. 제네릭이란?
다양한 타입에서 작동하는 컴포넌트를 작성할 수 있는 문법이다.
타입스크립트 문서에서는 아래와 같이 정의하고 있다.
C#과 Java 같은 언어에서, 재사용 가능한 컴포넌트를 생성하는 도구 상자의 주요 도구 중 하나는 제네릭입니다, 즉, 단일 타입이 아닌 다양한 타입에서 작동하는 컴포넌트를 작성할 수 있습니다. 사용자는 제네릭을 통해 여러 타입의 컴포넌트나 자신만의 타입을 사용할 수 있습니다.
https://www.typescriptlang.org/ko/docs/handbook/2/generics.html
Documentation - Generics
Types which take parameters
www.typescriptlang.org
2. 타입이 고정적일 때
//TypeScript
const name = <string>"이름";
console.log(name);
//결과
//이름
// => 이렇게 쓰는 경우는 없을 거다. 다만, dom을 이용해야하는 경우는 필요할 수도 있다.
위의 예시는 name 변수를 string 타입으로 밖에 못 쓰도록 해놓았다.
사실, 위처럼 사용하는 경우는 거의 없다.
왜냐하면, 제네릭은 다양하게 적용해야할 타입에 대해서 불편함 없이 사용하는 데 좋기 때문이다.
3. 타입이 고정적이지 않을 때
//TypeScript
type Name = [
{
name: string;
}
];
let array: Name= [{name: "park"}];
const insertData = <T extends object>(data: T, array: object[]): object[] => {
array.push(data);
return array;
}
console.log(insertData({name: "gunbro"}, array));
여기서 insertData의 data는 T 타입을 갖는데, T 타입은 제네릭 타입변수이며 object를 상속받았다.
왜냐하면, Name 타입에 객체 타입만 저장할 수 있다고 alias type으로 정의했기 때문이다.
//TypeScript
function merge<T, U>(objA: T, objB: U) {
return Object.assign(objA, objB);
}
const mergedObj = merge<{ name: string; hobbies: string[] }, { age: number }>(
{ name: "Park", hobbies: ["Sports"] },
{ age: 25 }
);
console.log(mergedObj);
//결과: {name: "Park", hobbies: ["Sports"], age: 25}
위 경우는 최종적으로 Object가 만들어진다.
하지만, Object의 프로퍼티 값의 타입은 정해지지 않았다.
이럴 때 제네릭은 빛을 발한다.
내가 원하는 타입을 넣을 수가 있어서 실수를 방지할 수 있으며, 동적이다.
이점은 Object의 세부적인 프로퍼티 값의 타입은 무엇이 들어갈지
잘 모르기 때문에 안정적이며, 융통성이 있다.
4. 제네릭 타입 변수를 key 타입 상속받기
제네릭은 안정성과 융통성을 주는 좋은 문법이다.
하지만, 우리가 알고 있던 상식적인 것이 여기서는 엄격하게 설정해야 한다.
//TypeScript
function extractAndConvert<T extends object, U extends keyof T>(
obj: T,
key: U
) {
return obj[key];
}
console.log(extractAndConvert({ name: "gunbro" }, "name"));
//결과: gunbro
여기서 keyof T가 없다면, 아래와 같은 오류가 발생한다.
'U' 형식을 인덱스 형식 'T'에 사용할 수 없습니다.ts(2536)
5. 제네릭 클래스
클래스에 여러가지 데이터가 들어간다면, 어떻게 해야할까?
제네릭을 쓰는 게 좋을 것이다.
//TypeScript
class DataStorage<T extends number | string | boolean> {
private data: T[] = [];
addItem(item: T){
this.data.push(item);
}
removeItem(item: T){
this.data.splice(this.data.indexOf(item), 1);
}
getItems(){
//1단계 깊은 복사: instance를 그대로 복사해서 사용하더라도 복사된 instace에 피해주지 않기 위해
return [...this.data];
}
}
const stringStorage = new DataStorage<string>();
stringStorage.addItem('gunbro');
stringStorage.addItem('youngggo');
stringStorage.addItem('parkminjeong');
stringStorage.removeItem('youngggo');
console.log(stringStorage.getItems());
//결과: ['gunbro', 'parkminjeong']
stringStorage.addItem(10);
//결과: 오류
한 번 제네릭 클래스를 정의해두고, 타입을 결정한다면, 그 타입으로 유지해야한다.
그렇게 유지하지 않는다면, 아래와 같은 오류가 발생한다.
예) 인자를 string data로 넣지 않고, number data로 넣었을 때
'number' 형식의 인수는 'string' 형식의 매개 변수에 할당될 수 없습니다.
6. 유틸리티 타입
유틸리티 타입은 이미 정의해놓은 타입을 변환할 때 사용된다.
아무리 제네릭을 사용하더라도 유동성의 한계가 있으니,
유틸리티 타입이 있는 것 같다.
6-1. Partial
interface Animal{
name: string;
age: number;
};
//인터페이스를 이용해서 타입을 부착해주었을 때
//빈 객체로 할당해주면, 오류 발생!
const cat: Animal = {};
//Partial 유틸리티 타입을 이용해서 타입을 부착해주었을 때
//빈 객체로 할당해주어도, 오류 발생하지 않음O
const cat: Partial<Animal> = {};
type Cat = Partial<Animal>;
const munchkin: Cat = {
name: "코코"
};
const persian: Cat = {
name: "몽쉘",
age: 1
};
원래 Partial없이 사용했다면, muchkin 할당했을 때부터 오류가 났을것이다.
6-2. Pick
//TypeScript
interface Animal{
name: string;
age: number;
}
//하나의 속성만 허락할 때
type Cat = Pick<Animal, "name">
const munchkin: Cat = {
name: "gunbro",
//name말고는 더 이상 정의 할 수 없다.
}
console.log(munchkin);
//결과: {name: "gunbro"}
//2개 이상의 속성을 허락할 때
type Cat = Pick<Animal, "name" | "age">;
const persian: Cat = {
name: "몽쉘",
age: 1,
//name과 age 속성이 없으면 오류가 난다.
};
Pick 타입의 경우는 객체의 프로퍼티에서 꼭 있어야하는 프로퍼티만 따로 타입으로
만들 수 있음을 보여주는 문법이다.
6-3. Omit
//TypeScript
interface Animal{
name: string;
age: number;
}
//Cat을 사용하는 변수는 Omit type에 있는 두 번째 인자인 age를 가질 수 없다.
type Cat = Omit<Animal, "age">
const munchkin: Cat = {
name: "gunbro",
//age를 사용하면 오류가 발생한다.
}
console.log(munchkin);
Omit 타입은 Pick 타입과 반대이다.
어떤 인터페이스의 프로퍼티들이 많고, 몇 개만 생략하고 싶을 때
Omit 타입을 사용하면 된다.
7. Readonly
const names: Readonly<string[]> = ["gunbro", "youngggo"];
names.push("Park");
//names를 읽기만 가능하기 때문에 2번째 줄은 오류가 발생한다.
끝!
'관심있는 언어들 > TypeScript' 카테고리의 다른 글
[TypeScript] 고급 타입2 (0) | 2022.01.13 |
---|---|
[TypeScript] 고급 타입1 (0) | 2022.01.06 |