TypeScript

대수 타입

김귤🐵 2023. 6. 11. 21:34

 

 


 

 

1.대수 타입(Algebraic Types)이란?

대수 타입이란 여러 타입을 조합하여 새로운 타입을 만들 수 있는 기능을 말한다.

종류로는 유니온 타입(Union Types),인터섹션 타입(Intersection Types)이 있다.

타입을 집합으로 생각할 때, 집합 간의 연산을 표현하는 방식으로 이해할 수 있다.

유니온 타입은 합집합에 해당하고 인터섹션 타입은 교집합에 해당한다.

 

1️⃣인터섹션 타입-교집합

인터섹션 타입은 &을 이용해서 교집합 타입을 만든다. 이러한 교집합 타입은 객체타입에서 많이 사용된다.

일반 원시값으로 사용하면 서로 교집합 하는 부분이 없기 때문에 never를 반환하게 된다. 

 

👉🏻원시 타입

let variable : number & string

👉🏻객체 타입

Type Dog = {
	name:string;
	howling:boolean;
}
Type Cat = {
	name:string;
	grooming:boolean;
}

type Intersection = Dog & Cat

let intersection:Intersection = {
  name:'개냥이',
  howling:true,
  grooming:true,
}

교집합인 name,howling,grooming의 속성을 만족하는 값만 들어올 수 있게 된다. 

 

2️⃣유니온 타입-합집합

여러 개의 타입을 하나의 타입으로 결합하는 방법이다. 

기호(|)를 사용하여 타입들을 나열해 표현한다. 

👉🏻기본 타입

let a : string | number;
a = 1;  //ok!
a = "hello"; //ok!

string과 number 모두를 포함하기 때문에 둘 다 사용가능.

 

👉🏻객체 타입

type Dog = {
  name:string;
  howling:boolean
}
type Cat = {
  name:string;
  grooming:boolean;
}
type Mixed = Dog | Cat 

const cat:Mixed = {
	name:"고양이",
	grooming:true,
}
const dog:Mixed = {
	name:"강아지",
	howling:true,
}
const dogCat:Mixed = {
	name:"개냥이",
	howling:true,
	grooming:true
}



이렇게 {name,howling}, {name,grooming} , {name,howling,grooming}  

교집합 까지 포함한 세 가지의 형식을 사용할 수 있는 것이다.

 

2.서로소 유니온 타입(disjoint union type)

서로 중복되지 않는 타입들의 합집합을 표현하는 방법이다. 각각의 타입은 서로 독립적이며, 

공통된 속성이 없는 타입들을 조합하여 새로운 타입을 만들 수 있다.
이런 서로소 유니온 타입에서는 구별된 유니온(Discriminated Union)이라는 것을 사용할 수 있다.

이것은 공통된 속성을 사용하여 각각의 타입을 식별할 수 있도록 하여 가독성와 유지보수성을 향상시킨다.

type Dog = {
  name: string;
  howling: boolean;
};

type Cat = {
  name: string;
  grooming: boolean;
};

type Bird = {
  name: string;
  wing: boolean;
};

type Pet = Dog | Cat | Bird;

이러한 서로소 유니온 타입이 있다.

function animal(pet: Pet) {
  if ("howling" in pet) {
    console.log(`${pet.name}는 하울링을 합니다.`);
  } else if ("grooming" in pet) {
    console.log(`${pet.name}는 그루밍을 합니다.`);
  } else if ("wing" in pet) {
    console.log(`${pet.name}는 날개가 있습니다.`);
  }
}

매개 변수로 들어오는 pet에 따라 출력이 변경되도록 하기 위해 타입가드를 사용하여 타입을 좁혀줄 수 있다.

이렇게 동작하는것에는 문제는 없지만 구별된 유니온을 사용하여 가독성을 높일 수 있다.

type Dog = {
  type: "강아지";
  ...
};

type Cat = {
  type: "고양이";
  ...
};

type Bird = {
  type: "새";
  ...
};

이렇게 공통의 속성인 type을 추가하고 이를 문자열 리터럴로 정의해준다.

이것을 이용해서 다시 타입가드의 가독성을 높일 수 있다.

function animal(pet: Pet) {
  if (pet.type === "강아지") {
    console.log(`${pet.name}는 하울링을 합니다.`);
  } else if (pet.type === "고양이") {
    console.log(`${pet.name}는 그루밍을 합니다.`);
  } else if (pet.type === "새") {
    console.log(`${pet.name}는 날개가 있습니다.`);
  }
}

또는 switch문을 사용해서 만들 수 있다.

function animal(pet: Pet) {
  switch(pet.type){
    case "강아지":{
      console.log(`${pet.name}는 하울링을 합니다.`);  
      break;
    }
    case "고양이":{
      console.log(`${pet.name}는 그루밍을 합니다.`);
      break;
    }
    case "새":{
      console.log(`${pet.name}는 날개가 있습니다.`);      
      break;
    }
  }
}

이렇게 구분이 가능한것은 속성에 스트링 리터럴을 사용했기 때문이다. 

교집합이 생기기 위해서는 똑같은 스트링 리터럴을 가져야 하는데 그것은 불가능 하기 때문에

집합의 관계들이 완전한 서로소 집합이 되어버린다.