[TypeScript] 인터페이스 & 유니온 타입 & 인터섹션 타입

Main·2023년 9월 4일
0

TypeScript

목록 보기
2/8
post-thumbnail

인터페이스(interface) 란?

타입스크립트에서 객체 타입을 정의할 때 사용하는 문법입니다.
인터페이스를 사용하여 객체의 속성과 들어갈 데이터 타입을 정확하게 정의할 수 있습니다.


인터페이스로 타입을 정의할 수 있는 부분

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

인터페이스 사용 방법

  • interface는 interface 키워드를 사용해서 정의합니다.
  • 중괄호('{ }') 안에 객체 속성과 속성 타입을 정의합니다.
  • 정의한 인터페이스와 타입이 맞지 않는 객체에 인터페이스를 지정한다면 에러가 발생합니다.
  • 정의한 인터페이스 객체 속성이 모두 정의되어 있지 않다면 에러가 발생합니다.
  • 정의한 인터페이스 외의 객체 속성이 추가되어 있다면 에러가 발생합니다.
interface Profile {
 name: string,
 age: number,
 address: string,
}

let jon: Profile = {
  name: "jon",
  age: 20,
  address: "seoul",
}

// 에러 발생: age의 타입이 interface에 선언한 타입과 불일치 
let mike: Profile = {
  name: "mike",
  age: "20",
  address: "Busan"
}
  
// 에러 발생: address가 존재하지 않음 interface의 객체 속성과 불일치 
let jane: Profile = {
  name: "jane",
  age: 20
}
  
// 에러 발생: interface에 존재하지 않는 객체 속성 hobby가 추가됨
let bob: Profile = {
  name: "bob",
  age: 20,
  hobby: "shopping"
}
  

인터페이스 함수 타입 정의

1 ) 함수 파라미터 정의 방법

interface Food {
  name: string,
  prcie: number,
}
  
function foodInfo(someFood: Food) {
  console.log(someFood.name, someFood.price)
}
  
let pizza = {name: "pizza", "15000"}
foodInfo(pizza) // 'pizza', 15000

2 ) 함수의 반환 타입 정의 방법

interface Food {
  name: string,
  prcie: number,
}

function foodInfo(someFood: Food): Food {
  retrun pizza
}
  
let pizza = {name: "pizza", "15000"}
foodInfo(pizza) // {name:'pizza', prcie: 15000}

인터페이스의 옵션 속성(?)

인터페이스 속성 중 하나만 꼭 필요한 속성이고, 나머지 속성은 필요하지 않을 수 있는 속성일 경우 어떻게 정의해야 할까요?
타입스크립트에서는 타입과 객체의 속성이 일치하지 않으면 에러가 발생합니다.
인터페이스에서 옵션 속성(?)을 통해 객체의 속성을 꼭 받을 필요 없도록 설정할 수 있습니다.
옵션 속성은 상황에 따라 유연한 인터페이스 속성의 사용 여부를 결정할 수 있게 해줍니다.

interface Profile {
  name: string,
  age: number,
  address?: string, // 옵션 속성 사용 해당 값에 들어갈 수도 안들어갈 수도 있음
}

// 옵션 속성을 사용하여 address 값이 없어도 에러가 발생하지 않는다.
let jon: Profile = {
  name: 'jon',
  age: 20
}

인터페이스의 상속

  • 인터페이스는 상속으로 타입을 확장할 수 있습니다.
  • extends 키워드를 사용하여 상속할 수 있습니다.
interface Person {
  name: string,
  age: number,
}

 // Person 인터페이스를 상속(name, age)
interface Developer extends Person {
  skill: string;
}
  
// Devloper 인터페이스는 Person 인터페이스를 상속 받아 name과 age 속성 타입이 정의됨
let jon: Developer = {
  name: 'jon',
  age: 21,
  skill: 'typescript'
}

인터페이스 상속 사용시 주의사항

  • 인터페이스 사용시 상위 인터페이스의 타입을 하위 인터페이스에서 상속받아 타입을 정의할 때 상위 인터페이스의 타입과 호환이 되어야 합니다.
  • 인터페이스를 상속해서 사용할 때는 부모 인터페이스에 정의된 타입을 자식 인터페이스에서 모두 보장해 주어야합니다.
interface Person {
  name: string,
  age: number,
}

 // age 타입이 상속 받은 Person age의 타입과 일치하지 않아 에러발생
interface Developer extends Person {
  age: string,
  skill: string;
}

인터페이스 인덱싱 타입 정의

인터페이스로 객체와 배열의 인덱싱 타입을 정의할 수 있습니다.
인덱싱이란 객체의 특정 속성을 접근하거나 배열의 인덱스로 특정 요소에 접근하는 동작을 의미합니다.
배열의 인덱싱은 대괄호([ ])에 해당되는 인덱스를 넣어 사용합니다.
객체의 인덱싱은 대괄호([ ]) 프로퍼티 연산자를 사용합니다.
객체는 대괄호 프로퍼티 연산자를 사용할 때 대괄호 안에 해당 속성의 키값을 넣게됩니다.

인덱싱 사용 예시 코드

let user = {
 name: 'jon',
 age: 20,
}
// 대괄호 프로퍼티 연산자를 사용하여 인덱싱하여 값에 접근
// user.name, user.age와 같은 기능
console.log(user['name'], user['age']);

let companies = ['네이버', '토스', '당근마켓']
console.log(companies[0]) // 네이버

인터페이스에서의 배열 인덱싱 사용 예시 코드

interface StringArray {
  [index: number]: string;
}

let companies:StringArray = ['네이버', '토스', '당근마켓']

인터페이스에서의 객체 인덱싱 사용 예시 코드

interface Menu {
  [name: string]: number;
}

let foodMenu: Menu = {
  pizza: 15000,
  cola: 2000
}

let prcie = foodeMenu['pizza']; // 15000

객체 인덱싱 인덱스 시그니처 (index signature)

위에서 객체의 인덱싱을 통해 인터페이스를 정의했습니다.
객체의 인덱싱을 통해 타입과 속성 값을 정의한 것을 인덱스 시그니처라고 부릅니다.

  • 인덱스 시그니처는 정확한 속성 이름을 명시하지 않고 속성 이름의 타입과 속성 값의 타입을 정의하는 문법을 의미합니다.
  • 인덱스 시그니처를 사용하면 직접 인터페이스에 객체 타입을 지정하지 않아도 무수히 많은 속성 타입들을 추가할 수 있습니다.
  • 인덱스 시그니처는 코드를 작성할 때 구체적으로 어떤 속성이 제공될지 알 수 없어 코드 자동 완성이 되지 않습니다. 대신 정확히 들어갈 속성은 따로 지정해주어 자동 완성되도록 할 수 있습니다.
  • 인덱스 시그니처는 속성 이름은 모르지만 속성 이름의 타입 값과 속성 값의 타입을 아는 경우 활용합니다.
interface Menu {
  [name: string]: number;
}

let foodMenu: Menu = {
  pizza: 15000,
  cola: 2000,
  chicken: 15000,
  hamburger: 8000,
  //    .
  //    .
  //    .
  // 객체 안에 무수히 많은 속성이 있더라도 추가가 가능
}

유니온 타입(union type)이란?

여러 개의 타입 중 한 개의 타입을 사용하고 싶을 경우 사용하는 문법입니다.
유니온 타입은 중복된 코드를 줄이고, 타입을 더 정확히 선언할 수 있습니다.

유니언 타입의 사용 예시

유니언 타입은 | 연산자를 사용하여 선언하는 방식입니다.
아래 예시 코드에서 문자열, 숫자형 타입 모두 넘기고 싶을때는 유니온 타입을 사용할 수 있습니다.

function logText(text:string | number) {
  console.log(text);
}

logText('hi');
logText(10);

만약 유니언 타입이 없었다면 any 타입을 사용하지 않고서는 아래와 같이 두 개의 함수를 정의해야했을겁니다. 이런 경우 동일한 동작을 하는 함수이지만 타입만이 달라서 코드를 중복해서 작성해야한다는 매우 불편한 단점이 있습니다.
따라서 유니언 타입 사용은 중복된 코드를 줄일 수 있습니다.

function logText(text:string) {
  console.log(text);
}

function logNumber(text:number) {
  console.log(text);
}

logText('hi');
logNumber(10);

그럼 만약 유니언 타입을 사용하지 않고 any타입을 사용한다면 어떻게 될까요?
물론 any 타입을 사용하면 정상적으로 동작할 수 있습니다.
하지만 any 타입의 경우에는 모든 타입이 들어갈 수 있기 때문에 예측하지 못할 동작을 할 가능성이 생기며, 이는 타입스크립트를 사용하는 의미가 사라지게 됩니다. 또한, 타입스크립트의 타입이 정해져 있을 때 자동으로 속성과 API를 완성하는 기능을 사용할 수 없습니다.
따라서 유니언 타입 사용은 원하는 타입들 만을 더 정확히 선언할 수 있도록 해줍니다.

function logText(text:any) {
  console.log(text);
}

logText('hi');
logNumber(10);

유니온 타입 사용 주의사항

유니온 타입 사용시 함수에 어떤 값이 들어올지 알 수 없습니다.
어느 타입이 넘어올지 알 수 없기 때문에 문제가 없는 공통된 속성만 사용할 수 있습니다.

interface Person {
  name: string;
  age: number;
}

interface Devloper {
  name: string;
  age: number;
  skill: string;
}

// 에러 발생 Person에는 skill 타입이 없기 때문에
function introduce(someone: Person | Developer) {
  console.log(someone.skill);
}

만약 공통된 속성이 아닌 해당 타입에 존재하는 속성을 사용하고 싶을 경우에는 in 연산자를 사용해서 로직을 작성하면 됩니다.
in 연산자는 객체 특정 속성이 있는지 확인하는 자바스크립트 연산자입니다.
if문 안에 in 연산자를 사용하면 해당 속성이 있는 타입으로 간주됩니다.
이렇게 동작하는 방식을 타입가드(type guard) 라고 합니다. 타입가드의 내용은 추후 다루도록 하겠습니다.

interface Person {
  name: string;
  age: number;
}

interface Devloper {
  name: string;
  age: number;
  skill: string;
}

// in 연산자를 사용하여 someone 타입을 Developer 타입을 간주
function introduce(someone: Person | Developer) {
  if('skill' in someone) {
    console.log(someone.skill);
  }
}

인터섹션 타입(intersection type)이란?

인터섹션 타입은 두 개의 타입를 하나로 합쳐서 사용할 수 있는 타입입니다.
주로 인터페이스 2개를 합치거나 타입 정의 여러 개를 하나로 합칠 때 사용합니다.
합친 두 타입 속성 중 하나라도 빠지면 에러가 발생합니다.

interface Person {
  name: string;
  age: number;
}

interface Devloper {
  name: string;
  age: number;
  skill: string;
}

function introduce(someone: Person&Developer) {
 console.log(someone.name, somone.age, someone.skill);
}

let jon = {
 name: 'jon',
 age: 20,
 skill: 'typescript'
}

let mike = {
 name: 'jon',
 age: 20,
}

introduce(jon) // 'jon', 20, 'typescript'
// 에러 발생 skill 속성이 없음
introduce(mike)

정리

  • 인터페이스는 객체 타입을 정의하기 위해서 사용되는 문법입니다.
    • 인터페이스에서는 옵션속성을 사용하여 필요한 속성만을 정의하도록 할 수 있으며, 상속을 사용할 수 있습니다.
    • 인터페이스를 인덱스 타입으로 정의하여 인덱스 시그니처 구현해 직접 인터페이스에 객체 타입을 지정하지 않아도 무수히 많은 속성 타입들을 추가할 수 있습니다.

  • 유니온 타입은 여러 개의 타입 중 한 개의 타입을 사용하고 싶을 경우 사용하는 문법입니다.
    • 유니온 타입은 중복된 코드를 줄이고, 타입을 더 정확히 선언할 수 있습니다.

  • 인터섹션 타입은 두 개의 타입를 하나로 합쳐서 사용할 수 있는 타입입니다.
    • 주로 인터페이스 2개를 합치거나 타입 정의 여러 개를 하나로 합칠 때 사용합니다.
profile
함께 개선하는 개발자

0개의 댓글