앞서 작성한 포스팅(https://s-ryung.tistory.com/15)과 연관성이 높습니다.
Interfaces
앞서 배운 Type과 비교할 일이 많은 인터페이스
인터페이스는 object의 모양을 알려주는 방법이다.
Type vs Interface : 용도
type의 용도는 크게 3가지가 존재하는데, object의 모양을 알려주는 용도가 셋 중의 하나이다.
반면에 interface의 용도는 위의 저것. 단 하나의 용도다😮
Type의 용도 | Interface의 용도 |
object의 모양을 알려준다 | |
타입을 특정 값으로 제한 가능하다 | |
타입 Alias를 만들 수 있다 |
type의 세가지 용도를 예시와 함께 확인해보자.
// 용도3. alias 만들기
type Team = "read" | "blue" | "yellow"; // 용도2. 특정 값으로 제한하기
type Health = 1 | 5 | 10;
// 용도1. object의 모양 알려주기
type Player = {
nickname: string,
team: Team
health: Health
}
const ryung: Player = {
nickname: "ryung",
team: "yellow",
health: 10
}
그리고 interface의 사용 예시도 확인해보자.
단 한가지의 용도인, object의 모양을 알려주는 용도로
type Player 대신에 interface Player로 작성 가능하다.
type Team = "read" | "blue" | "yellow";
type Health = 1 | 5 | 10;
// 용도1. object의 모양 알려주기
interface Player {
nickname: string,
team: Team
health: Health
}
const ryung: Player = {
nickname: "ryung",
team: "yellow",
health: 10
}
객체지향 프로그래밍에 조금 더 초점이 맞춰진 Interface
Type vs Interface : 타입 상속
인터페이스의 기본 구조는 클래스와 꽤나 유사하다.
interface User {
name: string
}
interface Player extends User { /*...*/ }
const ryung: Player = {
name: "ryung"
}
기본 사용 구조에서 class 키워드 대신에 interface 키워드를 사용하는 정도의 차이만 존재한다.
인터페이스 User를 상속받는 인터페이스 Player를 표현할 때에도
클래스를 상속할 때와 같이 extends 를 사용한다.
타입의 경우는 상속하는 방식이 인터페이스와는 다르다.
type User = {
name: string
}
type Player = User & { /*...*/ }
const ryung: Player = {
name: "ryung"
}
- type Player = User & { ... }
- interface Player extends User { ... }
작성 방식이 아예 다르기 때문에 사용할 때 혼용하지 않도록 주의해야 한다.
Type vs Interface : property를 타입에 추가하기
이 시점에서 이상한 인터페이스의 기능도 한 번 소개해보겠다.
interface User {
name: string
}
interface User {
lastName: string
}
interface User {
health: number
}
interface User { } 를 중복 선언하면서 각각 다른 key의 타입을 알려주면 그것들을 모두 합쳐준다.
interface User {
name: string,
lastName: string,
health: number
}
위처럼 한 번에 작성한 것과 같다. 때문에 사용에도 문제 없다. 희않하뇌....,
const ryung: User = {
name: "ryung",
lastName: "yi",
health: 10
}
type은 위 같은 중복 선언이 불가능하다.
추상클래스와 Interface
앞서 배운 추상클래스를 한 번 복습해보자.
다른 클래스가 상속 받을 수만 있는 클래스를 추상클래스라고 정의했고 abstract 키워드를 사용한다.
추상클래스에는 특이점이 있는데
타입스크립트에서만 사용되는 보호장치이기 때문에 자바스크립트로 컴파일하고나면 일반 클래스로 바뀌어버린다.
추상클래스는 오로지 상속 받는 용도이기 때문에 직접 새로운 인스턴스를 추가할 수 없다고 학습했는데
컴파일된 자바스크립트에서는 인스턴스 추가가 가능해지는 것이다.
위험하다고 판단되는 이것을 막을 방법이 없을까하고 고민하니
클래스와 기본구조가 일치한다던 인터페이스가 떠오른다.
type alias와 interface는 JS 컴파일 시에 아예 삭제된다.
앞에서 abstract나 protected가 타입스크립트의 보호장치이기 때문에 컴파일 이후 사라지는 것과 같은 맥락이다.
이것을 활용해 추상클래스에 새로운 인스턴스가 추가되는 문제가 발생하지 않도록
자바스크립트로 컴파일하면서 추상클래스를 아예 삭제하는 방법을 선택해버릴 수 있다.
추상클래스를 인터페이스로 변경함으로써 말이다.
위에서 작성한 추상클래스 코드는 다음과 같다.
abstract class User {
constructor(
protected firstName: string,
protected lastName: string
) {}
abstract sayHi(name: string): string
abstract fullName(): string
}
class Player extends User {
fullName(){
return `${this.firstName} ${this.lastName}`;
}
sayHi(name: string){
return `Hello ${name}. My name is ${this.fullName}.`;
}
}
이제 abstract class를 대신해 interface를 사용해보자.
interface User {
fistName: string,
lastName: string,
sayHi(name: string): string
fullName(): string
}
class Player implements User {
constructor(
public firstName: string,
public lastName: string
) {}
fullName(){
return `${this.firstName} ${this.lastName}`;
}
sayHi(name: string){
return `Hello ${name}. My name is ${this.fullName}.`;
}
}
첫번째로
인터페이스 User 안에는 object의 모양을 잡아주기 위한 코드만 존재하도록
constructor를 상속클래스 Player의 밑으로 이동시켜준다.
그리고 이렇게 이동된 constructor의 property들이 private로 선언되어 있으면 인터페이스 User가 접근을 할 수 없어지기 때문에 public으로 변경해준다.
두번째로
클래스가 클래스를 상속할 때에는 extends를 사용했지만
클래스가 인터페이스를 상속할 때에는 implements를 사용해야하기 때문에 이 부분도 함께 변경해준다.
(어라라... 앞서 type이랑 비교할 때 extends 쓰지 않았나...? 라는 생각이 들었다면
그건 인터페이스가 인터페이스를 상속할 때였다. 기억하자!)
이렇게 인터페이스를 사용함으로써
자바스크립트 코드가 추상클래스를 사용해버려서 일으키는 문제를 방어할 수 있다.
덤으로 파일크기도 줄일 수 있게 됐다!
interface는 클래스의 모양을 알려주면서 JS 코드로 컴파일 되지 않는다는 면에서
더 유용하다 판단 할 수 있다. 때문에
다른 클래스들이 특정 모양을 따르도록 하기 위한 용도라면
추상클래스보다는 인터페이스 사용하는 것을 권유한다.
위 코드를 조금 더 활용해보자면
인터페이스를 추가로 선언해 두 개의 인터페이스를 상속받아 합치도록 할 수 있고,
인터페이스를 타입으로 지정해 사용할 수도 있다.
interface User {
fistName: string,
lastName: string,
sayHi(name: string): string
fullName(): string
}
interface Human { // 인터페이스 Human을 추가
health: number
}
class Player implements User, Human { // Human도 함께 상속
constructor(
public firstName: string,
public lastName: string
public health: number // 추가
) {}
fullName(){
return `${this.firstName} ${this.lastName}`;
}
sayHi(name: string){
return `Hello ${name}. My name is ${this.fullName}.`;
}
}
// 인터페이스 User를 타입으로 지정해 받기
function makeUser(user: User){
return "hi"
}
makeUser({
firstName: "ryung",
lastName: "yi",
fullName: () => "xx",
sayHi(name) => "string"
})
Type vs Interface : 결론
대체적으로는 class나 object의 모양을 정의하고 싶은 경우에는 interface, 그 외의 경우는 type을 사용한다.
하지만 개인 취향에 따라 선택해도 괜찮다고 한다.
공식문서를 통해서도 관련 내용을 읽어볼 수 있으니 참고하자.
참고
'TYPESCRIPT' 카테고리의 다른 글
[TYPESCRIPT] npm run dev하면 저절로 해결되는 타입 에러? (env.d.ts) (0) | 2023.11.20 |
---|---|
[TYPESCRIPT] Class를 이용해서 객체지향 프로그래밍 해보기 (1) | 2023.05.07 |
[TYPESCRIPT] Function Call Signatures (0) | 2023.05.04 |
[TYPESCRIPT] 타입스크립트 큰 맥락 파악하기 : 타입들 총 정리 (0) | 2023.05.03 |
[TYPESCRIPT] 추론적 타입과 명시적 타입 (0) | 2023.04.26 |