Typescript에서의 interface와 type

김명주·2023년 5월 20일
0
post-custom-banner

Typescript?

타입스크립트란 자바스크립트 기반의 정적 타입 언어이다. 자바스크립트로 변환 후 브라우저나 node.js 환경에서 동작한다.
자바스크립트의 경우 동적 타입 언어로써, 런타임에서 동작 할 때 오류를 확인한다. 반대로 타입스크립트는 정적 타입 언어로써, 컴파일 이전에서, 즉 코드 작성 단계에서 오류를 확인하고 수정할 수 있는 장점이 있다. 그래서 코드를 작성할 때 발생할 수 있는 오류를 줄이고 자바스크립트보다 더 나은 개발 속도와 환경을 얻을 수 있다.
주의할 점은, 타입스크립트 파일은 브라우저에서 직접 동작하지 못한다. parcel과 같은 번들러가 읽을 수 있도록 변환하는 과정이 있기 때문에 순수 자바스크립트보다는 약간 느릴 수 있다.

interface

인터페이스는 타입스크립트에서 객체 데이터의 타입을 지정하는 용도로 사용된다. 선언한 타입에 맞게 코드를 작성해야지만 오류가 발생하지 않는다.

interface User {
    name:string
    age:number
    isValid:boolean
}

const kim: User = {
    name:'carl',
    age:29,
    isValid:true
}

const nest : User = { // 'isValid' 속성이 '{ name: string; age: number; }' 형식에 없지만 'User' 형식에서 필수입니다.
    name:"choi",
    age:23
}

물음표(?) 기호를 사용해서 선택적 속성을 지정할 수 있고, 읽기 전용 속성인 readonly 도 추가할 수 있다.

interface User {
    name:string
    readonly age:number
    isValid?:boolean
}

const kim: User = {
    name:'carl',
    age:29,
    isValid:true
}

kim.isvalid = false; // 오류가 발생하지 않고 문제없이 변경 가능

kim.age = 40; // 읽기 전용 속성이므로 'age'에 할당할 수 없습니다

const nest : User = { // 위의 예시에서는 오류가 발생했지만, 선택적 속성 지정으로 오류가 발생하지 않는다.
    name:"choi",
    age:23
}

함수 타입 지정

인터페이스는 객체 타입을 지정할 때에 주로 사용되지만, 함수의 타입을 지정할 때도 사용할 수 있다.
함수의 타입을 지정할 땐 호출 시그니처(Call Signature)라는 문법을 사용해야 한다.
호출 시그니처란 인터페이스 안에서 소괄호를 이용하여 함수의 타입을 지정하는 것을 의미한다.

왜 인터페이스를 통해 함수의 타입을 만들어야 할까? 이는 재사용성을 위한 것이다. 만약 특정 함수의 타입을 지정한 인터페이스를 만들었는데 그 인터페이스와 동일한 구조의 함수가 있다면 사용할 때마다 만들 필요 없이 이미 만들어둔걸 가져오기만 하면 된다.

interface GetName{
    (message:string):string // 호출 시그니처인 소괄호를 통해서 함수의 타입을 지정할 수 있다.
}

interface User {
    name:string
    readonly age:number
    getName: GetName // (message:string) => string
}

const kim: User = {
    name:'carl',
    age:29,
    getName(message:string){
        console.log(message)
        return this.name
    }
}


kim.getName('Hello')

인덱스 가능 타입

대표적으로 배열이나 객체 데이터에 타입을 지정하는 경우다. 호출 시그니처와 달리 대괄호를 이용해 타입을 지정한다.
인덱스 가능 타입이라는 것은 대괄호 표기법을 통해서 숫자나 문자를 넣어 인덱싱 할 수 있는 데이터의 타입을 지정하는 것을 의미한다.

interface Fruits{
    [item:number]:string
}

const fruits:Fruits = ['Apple', 'Banana', 'Cherry']
console.log(fruits) //[ "Apple", "Banana", "Cherry" ]


interface User {
    name:string
    readonly age:number
    [key:string]:unknown // unknown은 값을 알 수 없을때 지정하는 타입
}

const kim: User = {
    name:'carl',
    age:29,
}

// 아래와 같이 kim이라는 객체데이터에 대괄호 표기법으로 새로운 값을 문자데이터를 인덱싱하고 할당할 수 있다.
kim['isValid'] =true
kim['email'] = ['oterte@naver.com']
console.log(kim) 
// age: 29
// email: Array [ "oterte@naver.com" ]​
// isValid: true

// name: "carl"

확장(상속)

말 그대로 다른 인터페이스의 내용을 상속할 수 있다.
또한 동일한 이름을 가진 interface를 만들 수 있다.

interface UserA{
    name:string
    age:number
}

interface UserB extends UserA{
    isValid:boolean
}

const kim: UserA={
    name:"carl",
    age:38,
    isValid:true // 개체 리터럴은 알려진 속성만 지정할 수 있으며 'UserA' 형식에 'isValid'이(가) 없습니다.
}
const choi: UserB={ // 이 인터페이스에는 name과 age 속성이 없지만, UserA에서 상속받앗기 때문에 사용이 가능하다.
    name:"carl",
    age:38,
    isValid:true
}



interface FullName {
    firstName:string
    lastName:string
}

interface FullName{
    middleName:string
    lastName:string
}

const fullName:FullName = {
    firstName:'tom',
    middleName:'sean',
    lastName:'John'
}

type

다른말로 타입 별칭이다. 보통 부여된 별칭을 통해 재사용을 하는 용도로 주로 사용된다. 단일 개체에 타입을 지정할 수도 있지만 인터페이스와 마찬가지로 객체에도 지정할 수 있다.

type TypeA = string
type TypeB = string | number | boolean

type User = {
    name:string
    age:number
    isValid:boolean
} | [string, number, boolean] // 객체 타입 또는 튜플 타입

const userA: User = {
    name:'kim',
    age:25,
    isValid:true
}

const userB : User = ['Neon', 40, false]

그렇다면 type과 interface중 무엇을 사용해야 하나?

일단 기능적으로는 차이는 없다. 하지만 type 별칭은 여러 형태의 데이터들의 타입을 별칭으로 지정하기 위해 사용하는 것이지만 interface는 객체 데이터만의 타입을 지정하기 위해 사용하는 것이니 interface를 사용하는 것을 권장한다고 한다.
둘의 차이점을 살펴보면

  1. 문법
  • type은 type이라는 키워드와 할당연산자(=) 기호를 이용하지만 interface는 interface라는 키워드와 이름을 선언하기만 하면 된다.
type TypeUser = {
    name:string
    age:number
    isValid:boolean
}

interface interfaceUser{
    name:string
    age:number
    isValid:boolean
}
  1. 확장
    확장할 때 type은 & 기호를 사용하지만 interface는 extends 키워드를 사용한다.
type PeopleType = {
  name: string
  age: number
}

type StudentType = PeopleType & {
  school: string
}


interface PeopleInterface {
  name: string
  age: number
}

interface StudentInterface extends PeopleInterface {
  school: string
}
  1. 동일한 이름을 가진다면?
    type은 동일한 이름을 사용할 수 없지만 interface는 가능하다.
    즉 type은 새로운 프로퍼티에 열려있지 않다는 것이고, interface는 항상 열려있다는 것이다.
type Window = {
  title: string
}

type Window = {
  ts: TypeScriptAPI
}

// Error: Duplicate identifier 'Window'.





// 인터페이스는 문제 없이 사용 가능
interface FullName {
    firstName:string
    lastName:string
}

interface FullName{
    middleName:string
    lastName:string
}

const fullName:FullName = {
    firstName:'tom',
    middleName:'sean',
    lastName:'John'
}

결론

type은 모든 타입을 선언할 때 사용될 수 있고, interface는 객체에 대한 타입을 선언할 때 사용될 수 있다. 둘 다 객체에 대한 타입을 선언하는 데 사용될 수 있는데, 확장 측면에서 사용 용도가 달라진다. 확장이 불가능한 타입을 선언하려면 type을 사용하면 되고, 확장이 가능한 타입을 선언하려면 선언 병합이 가능한 interface를 사용하면 된다.

profile
개발자를 향해 달리는 사람
post-custom-banner

0개의 댓글