2023년 01월 10일 Typescript 공식 문서를 기반으로 작성되었습니다.
✅ Type Alias(타입 별칭)란?
객체 타입이나 유니온 타입을 type annotation으로 직접 타입을 부여하는 것도 편하지만, 매번 타입 값을 부여해주어야 한다. type alias는 새로운 타입 값을 생성하는 것이 아닌 미리 정의한 타입을 나중에 쉽게 참고할 수 있게 이름을 부여하는 것과 같다.
구문
// string 타입을 사용할 때
const name: string = 'capt';
// 타입 별칭을 사용할 때
type MyName = string;
const name: MyName = 'capt';
string, number와 같은 간단한 타입뿐만 아니라 모든 타입에 이름을 지정할 수 있다.
type Developer = {
name: string;
skill: string;
}
type ID = number | string; // 유니온 타입
type Point = {
x: number;
y: number;
};
type SetPoint = (x: number, y: number) => void;
export type YesOrNo = 'Y' | 'N';
export type DayOfWeek = '월' | '화' | '수' | '목' | '금' | '토' | '일';
// 사용: const DayOfWeek: DayOfWeek = '월';
export enum DayofTheWeek = { '월', '화', '수', '목', '금', '토', '일' };
export type Name = string;
export type Email = string;
export type FooFunction = () => string;
YesOrNo(DayOfWeek도 마찬가지)처럼 특정한 값 자체를 기술해주면 문자열 Y 또는 N만 들어갈 수 있는 타입이 된다. string타입 보다 훨씬 구체적이다. 타입으로 기술된 것들은 컴파일 타임에 이 값이 들어가는지 안 들어가는지, 확인하는 용도이고, enum은 실제 데이터이다. 컴파일 타임이 아니라 런타임에 월 화 수 이런 값이 실제로 들어간다. enum타입은 특정 값으로 제한하는 기능은 유사하지만 실제 데이터이고, 타입 알리아스는 컴파일 타임의 타입만 검사하는 용도로 사용된다.
✅ Interface란?
인터페이스는 객체의 타입을 선언하는 방법이다. 오직 객체에만 사용된다.
구문
// type alias로 작성
type Point {
x: number;
y: number;
}
// interface로 작성
interface Point {
x: number;
y: number;
}
const pt: Point = {x: 123, y: 12}
function printCoord(pt: Point) {
console.log("The coordinate's x value is " + pt.x);
console.log("The coordinate's y value is " + pt.y);
}
printCoord({ x: 100, y: 100 });
위 인터페이스가 의미하는 것은 어떤 객체가 Point가 되기 위해서는 number 타입인 x와 y 프로퍼티를 가져야 한다는 것이다. 앞서 정의했던 타입 별칭과 동일하게 동작한다.
🟡 선택적 인터페이스 프로퍼티와 readonly 제어자
interface Person {
first: string;
last: string;
nickname?: string;
}
const user: Person = {first: 'Kim', last: 'jeongmin'}
옵셔널 체이닝을 이용하여 nickname 프로퍼티는 없어도 에러가 발생하지 않는다.
interface Person {
readonly id: number;
first: string;
last: string;
nickname?: string;
}
const user: Person = {id:1, first: 'Kim', last: 'jeongmin'}
user.first = 'park';
user.id = 2; // Cannot assign to 'id' because it is a read-only property.
객체를 생성할 때 ID를 지정하는 건 가능하지만 변경하는 건 불가능하다.
🟡 인터페이스 메서드
interface Person {
...
sayHi: () => string;
}
const user: Person = {id:1, first: 'Kim', last: 'jeongmin'} // error
문자열을 반환하는 sayHi 메서드를 추가하였다. user에는 sayHi 메서드가 없으니 당연하게도 에러가 발생했다.
const user: Person = {
...
sayHi: 'sss' // error
}
위에서 정의한 내용은 sayHI가 문자열이 아닌 메서드여야 한다.
interface Person {
...
sayHi: () => string;
sayBye(): string;
}
const user: Person = {
...
sayHi: () => 'Hello'
sayBye() {return 'Bye'}
}
위와 같이 작성해야 하며 다른 구문으로도 작성할 수 있다.
interface Product {
name: string,
price: number,
applyDiscount(discount: number): number;
}
const shoes: Product = {
name: 'blue Suede Shoes',
price: 100,
applyDiscount(amount: number) {
const newPrice = this.price * (1 - amount);
this.price = newPrice;
return this.price;
}
}
console.log(shoes.applyDiscount(0.4)) // 60
매개변수에 타입을 지정하여 사용할 수 있다.
🔥Type Alias VS Interface
본격적으로 Type Alias와 Interface를 비교해 보자.
🟢 1. Interface는 기존 Interface에 새로운 프로퍼티를 추가할 수 있다.
// interface 재선언
interface Person {
name: string;
};
interface Person {
name: string;
age: number;
};
const person: Person = {
name: 'jeong',
age: 23
}
Interface에서는 동일한 이름을 갖는 인터페이스 여러 번 선언할 수 있다. 이를 선언 병합(Declaration merging)이라고 한다. 재선언이 아닌 단일 인터페이스로 병합되는 것이다. 따라서, Interface로 선언된 타입은 선언 이후의 타입 확장에도 용이하다. Type Alias는 동일한 이름의 타입을 선언하면 에러가 발생한다. 선언 이후에는 해당 타입의 내용을 변경할 수 없음을 의미한다.
interface Cloner {
clone(animal: Animal): Animal;
}
interface Cloner {
clone(animal: Sheep): Sheep;
}
interface Cloner {
clone(animal: Dog): Dog;
clone(animal: Cat): Cat;
}
위 코드가 병합되어 아래와 같은 단일 코드가 된다.
interface Cloner {
clone(animal: Dog): Dog;
clone(animal: Cat): Cat;
clone(animal: Sheep): Sheep;
clone(animal: Animal): Animal;
}
🟢 2. Interface는 extends를 사용하여 확장된 타입을 선언할 수 있다.
인터페이스는 extends 키워드를 사용하여 명시적으로 기존 타입의 확장이 가능하다.
interface Dog {
name: string;
age: number;
}
// 선언 병합
interface Dog {
bark(): string;
}
const elton: Dog = {
name: 'Elton',
age: 0.5,
bark() {
return 'Woof Woof!';
},
};
// interface 확장
interface ServiceDog extends Dog {
job: "drug sniffer" | "bomb" | "guide dog";
}
name과 age 속성을 가졌던 Dog가 선언 병합을 통해 bark 메서드를 가지게 되었다. extends 키워드를 이용해 기존 Dog 속성의 name과 age, bark()에 job이라는 새로운 속성을 가진 새로운 Interface ServiceDog가 생성된 모습이다.
Type Alias는 교차를 통해 타입을 확장할 수 있다.
Intersection 키워드인 &을 사용하는 방식을 사용한다.
type Dog = {
name: string;
age: number;
}
// 타입 교차 확장
type ServiceDog = Dog & {
job: "drug sniffer" | "bomb" | "guide dog";
}
선언 병합은 불가능하기에 bark 메소드를 가지지 못하게 되었다.
🟢 3. 다중 상속
인터페이스를 확장할 때는 단일 상속 모델에 국한된 것이 아닌 다수의 인터페이스를 확장할 수 있다.
interface Person {
name: string
}
interface Employee {
readonly id: number,
email: string
}
interface Engineer extends Person, Employee {
level: string,
}
const poter: Engineer = {
name: 'poter',
id: 348,
email: 'poter@gmail.com',
level: 'senior'
}
Enginner는 Person과 Employee가 가진 속성을 모두 상속받았다. Type Alias로도 다중 상속을 할 수 있다.
type Person = {
name: string
}
type Employee = {
readonly id: number,
email: string
}
type Engineer = Person & Employee & {
level: string,
}
const poter: Engineer = {
name: 'poter',
id: 348,
email: 'poter@gmail.com',
level: 'senior'
}
🟢 4. Type Alias는 intersection(&), union(|) 키워드와 tuple 사용이 가능하다.
type PartialPointX = { x: number };
type PartialPointY = { y: number };
// intersection
type IntersectionPoint = PartialPointX & PartialPointY;
// union
type UnionPoint = PartialPointX | PartialPointY;
// tuple
type Data = [number, string];
정리
- 인터페이스는 객체만의 형태를 묘사할 수 있음. 즉, 모든 종류의 타입 별칭을 인터페이스로 대체할 수 없음.
- 인터페이스는 선언 병합이 가능하다.
- 인터페이스와 타입 별칭은 다중 상속을 구현할 수 있다.
- 타입 별칭은 유니온 타입과 튜플 형태의 타입도 선언할 수 있다.
결론
Docs에서는 Type Alias가 필요해지기 전까지는 Interface를 사용할 것을 권장하고 있다.
타입에 대한 설명을 풍성하게 많이 해 놓을수록 코딩을 하면서 실수할 수 있는 부분을 미연에 다 방지할 수 있기 때문에 굉장히 단단하고 안전한 코드를 만들 수 있다. 타이핑을 할 때, 타입을 작성할 때, 그만큼 많은 신경과 시간 투자를 하면, 이후에 실제 코드를 작성함에 있어서 오히려 시간을 버는 타입 스크립트의 최고의 장점이 될 것이다.
'개발자의 공부 > TS' 카테고리의 다른 글
[TS] Enums (0) | 2023.01.10 |
---|---|
[TS] Union Types + Literal (0) | 2022.12.20 |
[TS] Array Types (0) | 2022.12.20 |
[TS] 객체 타입 (0) | 2022.12.17 |
[TS]Object (0) | 2022.12.17 |