TypeScript의 타입은 연산자를 이용해 조합해서 사용할 수 있다. 다소 복잡한 타입을 표현하고 싶을 때, 지정한 여러 타입의 합집합을 의미하는 Union 타입과 교집합을 의미하는 Intersection 타입을 사용할 수 있다.
Union 타입은 |
연산자를, Intersection 타입은 &
연산자를 사용한다.
👾 Union Types
유니온 타입은 여러 타입 중 하나가 될 수 있는 값으로, 서로 다른 두 개 이상의 타입을 세로 막대 |
로 구분하여 만든다.
let value: string | number | boolean;
👩🏻💻 Union type 기본 예시
function printValue(value: number | string): void {
if (typeof value === 'number') {
// 이 분기에서 value는 'number' 타입을 가진다.
console.log(`The value is a number: ${value}`);
} else {
// 이 분기에서 value는 'string' 타입을 가진다.
console.log(`The value is a string: ${value}`);
}
}
printValue(10); // The value is a number: 10
printValue('hello'); // The value is a string: hello
💬 유니온 타입은 다양한 타입의 값을 처리해야 하는 경우 유용하다.
❗️ 만약 value: any
로 타입을 정의한다면 JavaScript로 작성하는 것과 큰 차이가 없다.
✔️ Union 타입 사용시 장점
Union 타입을 사용하면 타입을 추론할 수 있기 때문에, 타입에 관련된 API를 쉽게 자동완성으로 얻어낼 수 있다.
✔️ Union 타입 사용시 유의점
유니온 타입인 값이 있으면, 유니온 있는 모든 타입에 공통인 멤버들에만 접근할 수 있다.
interface Developer {
name: string;
skill: string;
}
interface Person {
name: string;
age: number;
}
function askSomeone(someone: Developer | Person) {
console.log(someone.name);
}
💬 askSomenone 함수 내부에서는 Developer와 Person이 갖고 있는 공통 프로퍼티인 name에만 접근할 수 있다.
만약 나머지 프로퍼티에도 접근하고 싶다면 타입 가드를 사용해야 한다.
✔️ 타입 가드(Type Guard) 사용하기
Type Guard란 TypeScript에서 타입을 보호하기 위해 사용되는 기능 중 하나이다. 타입 가드는 특정 코드 블록에서 타입의 범위를 제한해 해당 코드 블록 안에서 타입 안정성을 보장해 준다.
interface Developer {
name: string;
skill: string;
}
interface Person {
name: string;
age: number;
}
function askSomeone(someone: Developer | Person) {
if ('skill' in someone) {
console.log(someone.skill);
}
if ('age' in someone) {
console.log(someone.age);
}
}
askSomeone({ name: 'Hana', skill: 'Javascript' });
askSomeone({ name: 'Jay', age: 20 });
💬 in
연산자를 사용하여 해당 프로퍼티가 객체 내에 존재하는지 여부를 검사할 수 있다.
💬 유니온 타입은 공통되지 않은 프로퍼티(skill
age
)에 접근하기 위해 타입 가드를 해줘야 하지만 전달인자를 전달할 때 선택지가 생기게 된다.
✔️ 식별 가능한 유니언 타입
interface Car {
name: "car";
color: string;
start(): void;
}
interface Mobile {
name: "mobile";
color: string;
call(): void;
}
function getGift(gift: Car | Mobile) {
console.log(gift.color);
// 검사해야할 항목이 많아지면 switch를 사용하는게 가독성이 좋음.
if (gift.name === "car") {
gift.start();
} else {
gift.call();
}
}
getGift({ name:"car", color: "blue", start() { console.log("🚙 car") } })
// "blue", "🚙 car" 출력
getGift({ name:"mobile", color: "silver", call() { console.log("📞 call") } })
// "silver", "📞 call" 출력
( 👀 식별 가능한 유니언 타입, 이 부분은 사실 아래와 같이 이해하는게 맞는지 모르겠다. 혹시 아시는 분 있으시다면 댓글 부탁드립니다. 🙏🏻 )
💬 식별 가능한 유니언 타입 : 동일한 이름의 속성을 정의하고 (name), 그것의 타입을 다르게 줌으로써 (Car또는 Mobile) 두 개의 인터페이스를 구분할 수 있다. 위 예제에서 Car는 “car”타입, Mobile은 “mobile” 타입이 된다.
👾 Intersection Types
인터섹션 타입은 둘 이상의 타입을 결합하여 새로운 타입을 만드는 방법으로 & 로 표기한다. 기존 타입을 합쳐 필요한 기능을 모두 가진 단일 타입을 얻을 수 있다.
👩🏻💻 Intersection type 예시 코드
interface Colorful {
color: string;
}
interface Circle {
radius: number;
}
type ColorfulCircle = Colorful & Circle;
function draw(circle: ColorfulCircle) {
console.log(`Color: ${circle.color}, Radius: ${circle.radius}`)
}
draw({ color: "yellow", radius: 20 }) // "Color: yellow, Radius: 20"
interface Car {
name: string;
start(): void;
}
interface Toy {
name: string;
color: string;
price: number;
}
const toyCar: Toy & Car = {
name: "타요",
color: "blue",
price: 5000,
start() {},
}
console.log(toyCar) // { "name": "타요", "color": "blue", "price": 5000 }
interface Developer {
name: string;
skill: string;
}
interface Person {
name: string;
age: number;
}
type User = Developer & Person;
// User 변수는 Developer, Person 각각에 정의된 속성 모두를 받는다.
function askSomeone(someone: User) {
// askSomeone 함수 내에선 User 타입에 정의된 모든 프로퍼티에 접근할 수 있다.
console.log(someone.name);
console.log(someone.skill);
console.log(someone.age);
}
askSomeone({ name: "Elle", skill: "TypeScript", age: 27 });
// "Elle", "TypeScript", 27
💬 인터섹션 타입을 사용하면 타입을 연결해 하나의 단일 타입으로 표현할 수 있기 때문에, 타입 가드가 필요하지 않다.
💬 하지만 인터섹션 타입은 타입 가드는 필요없지만 전달인자를 전달할 때 모든 프로퍼티를 전부 보내주어야 한다. (위 예제에서는 name
skill
age
모두 전달해주어야 한다.)
👾 Literal Types
리터럴 타입을 사용하면 정해진 문자열이나 수치만 대입할 수 있는 타입으로 제어할 수 있다. 리터럴 타입은 그 자체만으로는 그다지 유의미하지 않지만 유니온 타입과 함께 사용하면 보다 유용하게 사용할 수 있다.
// 문자열 리터럴 타입
const userName = "Harry";
// 유니온 타입과 함께 사용시 특정 종류의 값들만을 정의하는데 사용할 수 있다.
let postStatus: "draft" | "published" | "deleted"
👩🏻💻 Literal type 기본 예시
type Job = "police" | "developer" | "teacher"; // 문자열 Literal type
interface User {
name: string;
job: Job;
}
const user: User = {
name: "Hanna",
job: "teacher"
// job은 "police" | "developer" | "teacher" 로 제한된다.
// 타입 선언에 없는 문자열이 할당되면 컴파일시 에러가 난다.
}
interface HighSchoolStudent {
name: string;
grade: 1 | 2 | 3; // 숫자 Literal type
}
const student: HighSchoolStudent = {
name: "Joy",
grade: 3
}
✏️ 공부하며 정리한 내용입니다. 잘못된 정보나 더 공유할 내용이 있으면 댓글로 알려주세요!
읽어주셔서 감사합니다 😊
'Frontend Dev > TypeScript' 카테고리의 다른 글
타입스크립트의 Enums(열거형) 타입에 대해 간단하고 빠르게 알아보자 (0) | 2023.07.30 |
---|---|
VSCode에서 TypeScript 환경 설정하기 (0) | 2023.07.26 |