
유니언 타입
|은 타입을 지정할 때 여러가지 타입을 연결하여 지정해둘 수 있는 방식으로 자바스크립트의 OR 연산자||와 같다 !
function printTest(text: string | number) {
if (typeof text === 'string') {
text.includes('a') // 문자 타입과 관련된 api 자동완성됨
}
if (typeof text === 'number) {
text.toFixed(); // 숫자 타입과 관련된 api 자동완성됨
console.log(text)
}
}
printTest함수는 파라미터로 문자열 혹은 숫자 타입을 전달받을 수 있다.printTest함수 내부에서는 인자로 받아오는 값의 type에 따라 다른 동작을 처리할 수 있다.
&연산자를 이용해 여러 개의 타입 정의를 하나로 합치는 방식
interface Animal {
name: string;
age: number;
}
interface Cat {
type: string;
age: number;
}
type pet = Animal & Cat;
pet 타입에는 Animal과 Cat타입들이 합쳐져서 할당된다.pet은 아래와 같이 정의된다{
name: string;
age: number;
type: string;
}
유니언 타입을 언어 그대로의 OR로 생각해서 인터페이스와 같은 타입을 다룰 때 오류를 범할 수 있다!
또는이라고 한다면 A인터페이스와 B인터페이스를 |를 통해 지정된 새로운 타입에서는 각 인터페이스가 가지는 서로 다른 속성들을 모두 사용할 수 있을 것 같지만 ... 실상은 그렇지 않다.
오히려! 둘 중 어느 타입이 올지 모르니 두 인터페이스가 공통으로 가지고 있는 속성에만 접근할 수 있다.
interface Animal {
name: string;
age: number;
}
interface Cat {
type: string;
age: number;
}
function myPet(pet: Animal | Cat) {
console.log(pet.age); // ⭕️
console.log(pet.type); // ❌ Cat에만 있는 속성이여서 타입 에러
console.log(pet.name); // ❌ Animal에만 있는 속성이여서 타입 에러
}
만약 유니언을 사용하면서 모든 속성에 접근하고 싶다면? 타입 가드를 통해 접근하여 사용할 수 있다.
타입 가드란?
데이터의 타입을 알 수 없거나, 될 수 있는 타입이 여러 개라고 가정할 때 조건문을 통해 데이터의 타입을 좁혀나가는 것
타입을 구별할 수 있는 단서가 있는 유니언 타입
type Animal {
name: string;
age: number;
}
type Cat {
type: string;
age: number;
}
function myPet(pet: Animal | Cat) {
if('name' in pet) {
console.log(pet.name);
} else {
console.log(pet.type);
}
}
name은 Animal에만 있으니 Animal 타입으로 추론 가능하다.아티클을 작성하면서 문득 .. type aliases과 interface의 차이가 무엇인지 궁금해졌고 .. 잠깐 주제를 벗어나 정리하고 가려고 합니다 ,,
🍀 type
- data의 형태를 정의할 수 있는 방법
- string, boolean, number, array, tuple, enum, advanced types 가진다.
🍀 type aliases
- 이미 존재하는 타입에 새로운 이름을 지정할 수 있는 방법 (새로운 타입을 정의하는 것은 아님)
type키워드를 통해서 사용할 수 있고 typescript 내에 존재하는 type이라면 모두 새로운 이름을 붙여줄 수 있다.type MyNumber = number; // number 타입을 MyNumber로 대신해서 사용 가능 // user가 가지는 타입들을 미리 정의해서 User로 대신 사용 가능 type User = { id: number; name: string; email: string; }
- 동일한 타입 필드를 가지더라도 여러개의 이름으로 사용할 수 있다.
type ErrorCode = string | number; type Answer = string | number; // 의도에 따라 type alias를 지정하여 코드를 더 읽기 쉽게 만들 수 있다!
🍀 interfaces
객체가 지켜야 하는 속성들을 정의해두는 것
interface Client { name: string; address: string; }하지만 위의 예시를
type alias로 동일하게 작성 가능하다type Client = { name: string; address: string; }이와 같은 경우에서는
type과interfaces중 어느 것을 사용해도 무방하지만 그래도type을 쓰는 경우와Interface를 쓰는 경우를 나누어볼 수 있다!
prmitive type에 alias를 적용하고 싶은 경우에는 type을 사용해야 한다. interface는 오직 객체에만 사용할 수 있다!
union type은 오직 type을 통해서만 정의될 수 있다.
interface 키워드를 사용하여 직접적으로 Union Type을 정의하는 것은 불가능하다.
(단, 2개의 interfaces를 사용해서 새로운 union type을 만드는 것은 가능하다.)
// ⭕️
type Transport = 'Bus' | 'Car' | 'Bike' | 'Walk';
// ❌ interface 키워드를 사용하여 직접적으로 Union Type을 정의
interface Shape {
kind: "square";
size: number;
} | {
kind: "circle";
radius: number;
};
// ⭕️
interface CarBattery {
power: number;
}
interface Engine {
type: string;
}
type HybridCar = Engine | CarBattery;
1️⃣ 인터페이스는 여러개의 인터페이스를 extends 키워드를 통해 확장할 수 있다.
interface VIPClient extends Client {
benefits: string[]
}
인터페이스가 아닌 type 타입도 확장이 가능하다. type Client = {
name: string;
};
interface VIPClient extends Client {
benefits: string[]
}
cf) 다만 유니언 타입은 어느 것이 확장될지 모르기 때문에 확장 불가능하다.
type은 intersection &을 사용하여야한다.// 여기서 Client는 type alias를 통해 정의되었다고 가정
type VIPClient = Client & {benefits: string[]};
2️⃣ 확장을 통해 새롭게 정의된 인터페이스는 기존 인터페이스의 속성과 메소드들에 모두 접근 가능하고 새롭게 추가할 수 있다.
이때 interface는 동일한 key값이 있는 경우 에러를 발생시킨다.
interface Person {
getPermission: () => string;
}
// Interface 'Staff' incorrectly extends interface 'Person'
interface Staff extends Person {
getPermission: () => string[]; // 동일한 이름 에러 발생
}
반면 type aliases에서는 동일한 key가 있어도 에러를 발생시키지 않고 자동으로 합쳐준다. 때문에 예상하지 못한 에러 발생에 주의해야 한다!
type Person = {
getPermission: (id: string) => string;
};
type Staff = Person & {
getPermission: (id: string[]) => string[];
};
const AdminStaff: Staff = {
getPermission: (id: string | string[]) =>{
return (typeof id === 'string'? 'admin' : ['admin']) as string[] & string;
}
}
mapped types, conditional types, type guards 등은 type을 통해서만 사용 가능하다. interface로는 사용할 수 없다!
Types vs. interfaces in TypeScript
특정 값들의 집합을 의미하는 자료형으로 숫자형과 문자형이 있다.
enum이름은 타입으로만 지정할 수 있다.
enum데이터는 타입으로 사용할 수 있고, 값으로도 사용할 수 있다.
enum Direction {
Up, // 0
Down, // 1
Left, // 2
Right // 3
}
enum Direction {
Up = 3 // 3
Down, // 4
Left, // 5
Right // 6
}
문자형 이넘은 숫자형 이넘과 거의 비슷하지만 이넘 값 전부 다 특정 문자 혹은 다른 이넘 값으로 초기화해주고 사용해야 한다.
enum Direction {
Up = "UP",
Down = "DOWN",
Left = "LEFT",
Right = "RIGHT",
}
let left = Direction.Left; // 'LEFT'서진 언니 아티클을 보면서 type aliases와 interface 차이가 더 궁금해져서 찾아보니까 합 타입 혹은 튜플 타입을 써야 되는 상황이 아니라면 interface가 선언병합이 가능해서 interface를 더욱 권장한다고 하네요! 깔끔하게 정리해준 덕분에 훨씬 이해가 잘 됐어요! 최고입니당👍제가 읽은 글도 아래 첨부해둬요!
https://medium.com/humanscape-tech/type-vs-interface-언제-어떻게-f36499b0de50
잘 봤습니다. 좋은 글 감사합니다.