저번 시간에 이어서 연산자를 이용한 타입 정의
에 대해 공부해보자.
타입스크립트 핸드북(타입스크립트 핸드북)을 보고 공부한 것을 정리한 글입니다.
기본적인 JS 지식은 가진 상태라고 가정합니다.
유니온 타입이란 자바스크립트의 OR 연산자(||)와 같이 A이거나 B이다 라는 의미의 타입이다. 아래 예제를 살펴보자.
function logText(text: string | number) {
// ...
}
위 함수의 파라미터 text
에는 문자열 타입이나 숫자 타입이 모두 올 수 있다. 이처럼 |
연산자를 사용해 타입을 여러 개 연결하는 것을 유니온 타입 정의 방식이라고 부른다.
유니온 타입의 장점은 아래 2개의 코드를 비교하면 바로 알 수 있다.
// any를 사용하는 경우
function getAge(age: any) {
age.toFixe(); // 에러 발생, age의 타입이 any로 추론되기 때문에 숫자 관련된 API를 작성할 때 코드가 자동 완성되지 않는다.
return age;
}
// 유니온 타입을 사용하는 경우
function getAge(age: number | string) {
if(typeof age === 'number') {
age.toFixed(); // 정상 동작, age의 타입이 `number`로 추론되기 때문에 숫자 관련된 API를 쉽게 자동완성 할 수 있다.
return age;
}
if(typeof age === 'string') {
return age;
}
return new TypeError('age must be number or string');
}
이처럼 any
를 사용하는 경우 마치 자바스크립트로 작성하는 것처럼 동작을 하고 유니온 타입을 사용하면 타입스크립트의 이점을 살리면서 코딩이 가능하다.
타입스크립트를 통해 타입 추론을 하면 자동완성이 되므로 코드 작성에서 오류 발생 가능성이 현저히 낮아진다!!
인터섹션 타입은 여러 타입을 모두 만족하는 하나의 타입을 의미한다. 아래 예제를 살펴보자.
interface Person {
name: string;
age: number;
}
interface Developer {
name: string;
skill: number;
}
type Capt = Person & Developer;
위 코드는 Person
인터페이스의 타입 정의와 Developer
인터페이스 타입 정의를 &
연산자를 이용하여 합친 후 Capt
라는 타입에 할당한 코드이다. 결과적으로 Capt
의 타입은 아래와 같이 정의된다.
{
name: string;
age: number;
skill: string;
}
이처럼 &
연산자를 이용해 여러 개의 타입 정의를 하나로 합치는 방식을 인터섹션 타입 정의 방식이라고 한다.
앞에서 유니온 타입과 인터섹션 타입을 살펴보았다. 아마 논리적으로 유니온 타입은 OR, 인터섹션은 AND라고 생각할텐데, 인터페이스와 같은 타입을 다룰 때에는 이런 논리적 사고를 주의해야한다.
아래 코드를 살펴보자.
interface Person {
name: string;
age: number;
}
interface Developer {
name: string;
skill: string;
}
function introduce(someone: Person | Developer) {
someone.name; // O 정상 동작
someone.age; // X 타입 오류
someone.skill; // X 타입 오류
여기서 introduce()
함수의 파라미터 타입을 Person
, Developer
인터페이스의 유니온 타입으로 정의하였다. 유니온 타입은 A도 될 수 있고 B도 될 수 있는 타입이지라고 생각하면 파라미터 타입이 Person
도 되고 Developer
도 될테니까 함수 안에서 당연히 이 인터페이스들이 제공하는 age
나 skill
을 사용할 수 있겠지라고 생각할 수 있다. 그러나, 타입스크립트 관점에서는 파라미터에 Person
이 올지 Developer
타입이 올지 알 수가 없기 때문에 어느 타입이 들어오든 간에 오류가 안 나는 방향으로 타입을 추론하게 된다.
const capt: Person = { name: 'capt', age: 100 };
introduce(capt); // 만약 `introduce` 함수 안에서 `someone.skill` 속성을 접근하고 있으면 함수에서 오류 발생
const tony: Developer = { name: 'tony', skill: 'iron making' };
introduce(tony); // 만약 `introduce` 함수 안에서 `someone.age` 속성을 접근하고 있으면 함수에서 오류 발생
결과적으로 introduce()
함수 안에서는 별도의 타입 가드(Type Guard)를 이용하여 타입의 범위를 좁히지 않는 이상 기본적으로는 Person
과 Developer
두 타입에 공통적으로 들어이쓴 속성인 name
만 접근할 수 있다.
function introduce(someone: Person | Developer) {
console.log(someone.name); // O 정상 동작
지금까지 연산자를 이용한 타입 정의에 대해 알아보았다.
조금 당연해보이지만 그냥 넘어갈 수 없는 부분이므로 잘 숙지하도록 하자.