4 분 소요

인터페이스

타입스크립트의 인터페이스는 두개의 시스템 사이에 상호 간에 정의한 약속 혹은 규칙을 포괄하여 의미한다. 혹은 객체의 껍데기 또는 설계도라고 할 수 있다.

즉, 프로그래밍에서 클래스 또는 함수의 ‘틀’ 을 정의하는 것처럼, 타입의 ‘틀’로서 사용할 수 있는 것이 인터페이스 인 것이다.여러 함수가 특정한 시그니처를 동일하게 가져야 할 경우 또는 여러 클래스가 동일한 명세를 정의해야하는 경우 인터페이스를 통해서 정의할 수 있다. 그래서 인터페이스에 선언된 프로퍼티 또는 메소드의 구현을 강제하여 각 클래스와 함수간의 일관성을 유지할 수 있도록 하는 것이다.

타입스크립트에서의 인터페이스는 보통 다음과 같은 범주에 대해 약속을 정의할 수 있습니다.

  • 객체의 스펙(속성과 속성의 타입)
  • 함수의 파라미터
  • 함수의 스펙(파라미터, 반환 타입 등)
  • 배열과 객체를 접근하는 방식
  • 클래스

인터페이스 사용하기

let user: object

user = {
  name: 'DO',
  age: 30,
}

console.log(user.name) //Property 'name' does not exist on type 'object'.

user라는 오브젝트에 name이라는 프로퍼티가 없다는 에러가 발생합니다.. 프로퍼티를 정의해서 오브젝트를 표현하고자 할 때는 인터페이스를 사용합니다.

interface User {
  name: string
  age: number
}

let user: User = {
  name: 'DO',
  age: 30,
}

console.log(user.name) // "DO"

인터페이스를 사용하면 더이상 에러가 발생하지 않습니다.

옵션 속성, Ootional Property

인터페이스를 사용할 때 인터페이스에 정의되어 있는 속성을 모두 다 사용해야 하는데, 있어도 되고 없어도 되는 옵셔널한 속성을 설정하려면 ? 를 붙이면 됩니다.

interface 인터페이스_이름 {
  속성?: 타입
}

📌 인터페이스가 가진 속성을 다 사용하지 않아 에러발생

interface User {
  name: string
  age: number
  gender: string
}

let user: User = {
  name: 'DO',
  age: 30,
}
// Property 'gender' is missing in type '{ name: string; age: number; }'
// but required in type 'User'.
interface User {
  name: string
  age: number
  gender?: string
}

let user: User = {
  name: 'DO',
  age: 30,
}

⇒ 옵션 속성으로 에러 해결

읽기전용 속성, Readonly Property

읽기 전용 속성은 인터페이스로 객체를 처음 생성할 때만 값을 할당하고 그 이후에는 변경할 수 없는 속성을 의미합니다. 문법은 다음과 같이 readonly 속성을 앞에 붙입니다. 값에 접근할 수 있지만 수정은 불가능합니다..

interface User {
  readonly birthYear: number
}

최초에 생성할 때만 할당이 가능하고 이 후에는 수정할 수 없습니다. 인터페이스로 객체를 선언하고 나서 수정하려고 하면 아래와 같이 오류가 납니다.

let user: User = {
  birthYear: 2000,
}
user.birthYear = 1999 // Cannot assign to 'birthYear' because it is a read-only property.

읽기 전용 배열

배열을 선언할 때 ReadonlyArray<T> 타입을 사용하면 읽기 전용 배열을 생성할 수 있습니다.

let arr: ReadonlyArray<number> = [1, 2, 3]
arr.splice(0, 1) // error
arr.push(4) // error
arr[0] = 100 // error

위처럼 배열을 ReadonlyArray로 선언하면 배열의 내용을 변경할 수 없습니다. 선언하는 시점에만 값을 정의할 수 있으니 주의해서 사용하세요.

Index signature

때로는 타입의 모든 속성 이름을 미리 알 수 없지만 값의 형태는 알고 있는 경우 사용됩니다. 즉, 모든 속성의 이름과 타입을 정확히 알지 못해도 그 형태만을 알고 있다면 해당 형태에 대한 타입을 지정할 수 있는 것을 의미합니다.

interface SomeType {
  [key: number]: string
}

위 예시에서 [key: string]는 Index Signature로, 이는 SomeType의 모든 속성 이름이 숫자형이고, 그 속성의 값은 문자열임을 나타냅니다. 이를 통해 모든 속성의 이름은 숫자이지만 그 값을 문자열로 제한함으로써 유연성을 유지할 수 있습니다.

Index Signatures는 TypeScript에서 객체의 속성을 동적으로 정의하는 데 유용하게 사용됩니다.

📌 학년별 성적을 저장한다면?

1~3학년의 학생들이 있다면 1학년은 1학년 성적만, 2학년은 1,2학년 성적이, 3학년은 1,2,3 학년 성적을 반영되게 하려고 합니다. number를 key로 하고, string을 value로 받는 프로퍼티로 설정해줄 수 있습니다.

interface User {
  [grade: number]: string
}

let user: User = {
  1: 'A',
  2: 'B',
}

Literal Types

TypeScript에 문자열이나 숫자에 정확한 값을 지정하여 더 엄격한 타입을 지정하는 것으로, 지정한 값 자체를 타입으로 지정하는 기능입니다.

위에 예제에서 성적을 string으로 하기에는 너무 광범위하므로, 성적을 A, B, C, F 로 지정해 줄 수 있습니다.

type Score = 'A' | 'B' | 'C' | 'F'

interface User {
  [grade: number]: Score
}

let user: User = {
  1: 'A',
  2: 'B',
  3: 'D', //Type '"D"' is not assignable to type 'Score'.
}

Score에서 지정한 문자열 외에 다른 문자열을 사용하게 되면 에러가 발생한다.

함수 타입

인터페이스로 함수를 정의할 수도 있다. 함수형 인터페이스를 사용하여 함수의 형태를 명시하고, 해당 형태를 따르는 함수를 구현할 수 있습니다.

📌 예제 1 : 숫자를 더하는 함수

interface Add {
  (num1: number, num2: number): number
}

Add 인터페이스는 두 개의 숫자를 입력으로 받아들이고, 숫자를 반환하는 함수의 형태를 나타냅니다. 이제 이 인터페이스를 구현하는 함수는 다음과 같이 정의할 수 있습니다:

const add: Add = function (x, y) {
  return x + y
}

add(10, 20)

📌 예제 2: 나이 값을 받아서 boolean을 리턴하는 함수

interface IsAdult {
  (age: number): boolean
}

const a: IsAdult = (age) => {
  return age > 19
}

a(33) // true
a(10) // false

클래스 타입

C#이나 자바처럼 타입스크립트에서도 클래스가 일정 조건을 만족하도록 타입 규칙을 정할 수 있습니다.

Car 라는 인터페이스를 정의하고, 이 Car 인터페이스를 통해서 BMW 라는 클래스를 만들어 볼 수 있습니다.

📌 예제 1.

interface Car {
  color: string
  wheels: number
  start(): void
}

class Bmw implements Car {
  color = 'pink'
  wheels = 4
  start() {
    console.log('Go...')
  }
}

const b = new Bmw()
console.log(b) // Bmw: { "color": "pink", "wheels": 4}
b.start() // "Go..."

📌 예제 2. 생성될 때 색상 입력받기

interface Car {
  color: string
  wheels: number
  start(): void
}

class Bmw implements Car {
  color
  wheels = 4
  constructor(c: string) {
    this.color = c
  }
  start() {
    console.log('Go...')
  }
}

const b = new Bmw('pink')
console.log(b) // Bmw: { "wheels": 4, "color": "pink"}
b.start() // "Go..."

인터페이스 확장(extends)

클래스와 마찬가지로 인터페이스도 인터페이스 간 확장이 가능합니다.

interface Car {
  color: string
  wheels: number
  start(): void
}

interface Benz extends Car {
  door: number
  stop(): void
}

const benz: Benz = {
  color: 'purple',
  wheels: 4,
  start() {
    console.log('Go...')
  },
  door: 4,
  stop() {
    console.log('Stop...')
  },
}

혹은 아래와 같이 여러 인터페이스를 상속받아 사용할 수 있습니다.

interface Car {
  color: string
  wheels: number
  start(): void
}

interface Toy {
  name: string
}

interface ToyCar extends Car, Toy {
  price: number
}

const mini: ToyCar = {
  color: 'pink',
  wheels: 4,
  start() {
    console.log('Go!')
  },
  name: 'Cooper',
  price: 5000,
}

참조


코딩앙마
https://joshua1988.github.io/ts/guide/interfaces.html

댓글남기기