number
나 string
을 매개변수로 기대하는 라이브러리가 존재한다고 가정해보자.
/**
* 만약 'padding'이 문자열이라면, 'padding'은 왼쪽에 더해질 것입니다.
* 만약 'padding'이 숫자라면, 그 숫자만큼의 공백이 왼쪽에 더해질 것입니다.
*/
function paddingLeft(value: string, padding: any) {
if(typeof padding === 'number') {
return Array(padding + 1).join(" ") + value;
}
if(typeof padding === 'string') {
return padding + value;
}
new Error(`Expected string or number, got '${padding}'.`);
}
paddingLeft("Hello World", 4);
paddingLeft
함수의 매개변수인 padding
이 any
타입으로 되어있다. number
나 string
이 아닌 인자로 함수를 호출할 수 있다는 뜻이고, 타입스크립트는 padding
이 any
타입이기 때문에 타입 검사를 안하기 때문에 문제가 없다고 받아들일 것이다.
any
타입 대신에 유니언 타입을 사용해서 padding
매개변수의 타입을 정의할 수 있다.
function paddingLeft(value: string, padding: number | string) {
// ...
}
유니언 타입은 여러 타입 중에 하나가 될 수 있는 값을 의미한다. 세로 막대(|)
로 각 타입을 구분하여 작성할 수 있다.
유니언 타입인 값이 존재하면, 모든 타입의 공통인 맴버들에만 접근이 가능
하다.
interface Bird {
fly(): void;
layEgges(): void;
}
interface Fish {
swim(): void;
layEgges(): void;
}
declare function getSmallPat(): Fish | Bird;
let pet = getSmallPat();
pet.layEgges(); // 정상, 두 인터페이스가 공통으로 가지고 있으므로 접근 가능
pet.swim(); // 오류, 두 인터페이스가 공통으로 가지고 있지 않으므로 접근 불가
타입 추론의 범위를 좁혀나가게 해줄 수 있는 리터럴 타입을 갖는 단일 필드를 사용해서 유니언을 구분할 수 있다.
아래 타입 들은 state
를 가지고 그들 각자만의 필드도 존재한다. 리터럴 타입으로 state
를 갖고 있다면 state
는 동일한 문자열과 대조되고 이후에는 타입스크립트는 현재 어떤 타입이 사용되고 있는지 알 수 있다.
interface NetworkLoadingState {
state: "loading";
}
interface NetworkFailedState {
state: "failed";
code: number;
}
interface NetworkSuccessState {
state: "success";
response: {
title: string;
duration: string;
summary: string;
}
}
type NetworkState =
| NetworkLoadingState
| NetworkFailedState
| NetworkSuccessState;
function networkState(state: NetworkState): string {
switch(state.state) {
case "loading":
return 'loading.....';
case "failed":
return `Error ${state.code}`
case "success":
return `${state.response.title} - ${state.response.summary}`
}
}
교차 타입은 여러 타입을 하나로 결합한다. 기존 타입들을 합쳐서 필요한 기능을 모두 가진 단일 타입을 얻을 수 있다.
예를 들어, 일관된 에러를 다루는 경우 에러 핸들링을 분리해서 하나의 응답 타입에 결합된 자체 타입으로 만들어 사용할 수 있다.
interface ErrorHandling {
success: beelean;
error?: {
message: string;
}
}
interface UsersData {
users: {
name: string;
}
}
interface PostsData {
posts: {
title: string;
}
}
type UsersResponse = UsersData & ErrorHandling;
type PostsResponse = PostsData & ErrorHandling;
const handleUsersResponse = (response: UsersResponse) => {
if(response.error) {
console.error('error : ', response.error);
return;
}
console.log('users : ', response.users);
}