TypeScript

05. 인터페이스

js0616 2024. 8. 27. 17:03

https://poiemaweb.com/typescript-interface

 

TypeScript - Interface | PoiemaWeb

인터페이스는 일반적으로 타입 체크를 위해 사용되며 변수, 함수, 클래스에 사용할 수 있다. 인터페이스는 여러가지 타입을 갖는 프로퍼티로 이루어진 새로운 타입을 정의하는 것과 유사하다.

poiemaweb.com

 

인터페이스는 여러가지 타입을 갖는 프로퍼티로 이루어진 새로운 타입을 정의하는 것과 유사하다.

 

인터페이스는 일반적으로 타입 체크를 위해 사용되며 변수, 함수, 클래스에 사용할 수 있다.

인터페이스에 선언된 프로퍼티 또는 메소드의 구현을 강제하여 일관성을 유지할 수 있도록 하는 것이다.

ES6는 인터페이스를 지원하지 않지만 TypeScript는 인터페이스를 지원한다.

인터페이스는 프로퍼티와 메소드를 가질 수 있다는 점에서 클래스와 유사하나 직접 인스턴스를 생성할 수 없고 모든 메소드는 추상 메소드이다. 단, 추상 클래스의 추상 메소드와 달리 abstract 키워드를 사용하지 않는다.

 

 

2. 변수

// 인터페이스의 정의
interface Todo {
    id: number;
    content: string;
    completed: boolean;
  }
 
  // 변수 todo의 타입으로 Todo 인터페이스를 선언하였다.
  let todo: Todo;
 
  // 변수 todo는 Todo 인터페이스를 준수하여야 한다.
  todo = { id: 1, content: 'typescript', completed: false };

 

 

3. 함수

함수의 인자와 return 에 대해서 타입을 정의 한다. 해당 인터페이스를 구현하는 함수는 정의된 타입을 준수해야됨. 

// 함수 인터페이스의 정의
interface SquareFunc {
    (num: number): number;
  }
 
  // 함수 인테페이스를 구현하는 함수는 인터페이스를 준수하여야 한다.
  const squareFunc: SquareFunc = function (num: number) {
    return num * num;
  }
 
  console.log(squareFunc(10)); // 100

 

 

4. 클래스 

인터페이스를 구현하는 클래스의 일관성을 유지할 수 있는 장점을 갖는다. 

// 인터페이스의 정의
interface IPerson {
    name: string;
    sayHello(): void;
  }
 
  /*
  인터페이스를 구현하는 클래스는 인터페이스에서 정의한 프로퍼티와 추상 메소드를 반드시 구현하여야 한다.
  */
  class Person implements IPerson {
    // 인터페이스에서 정의한 프로퍼티의 구현
    constructor(public name: string) {}
 
    // 인터페이스에서 정의한 추상 메소드의 구현
    sayHello() {
      console.log(`Hello ${this.name}`);
    }
  }
 
  function greeter(person: IPerson): void {
    person.sayHello();
  }
 
  const me = new Person('Lee');
  greeter(me); // Hello Lee

 

 

 

5. 덕 타이핑 

만약 어떤 새가 오리처럼 걷고, 헤엄치고, 꽥꽥거리는 소리를 낸다면 나는 그 새를 오리라고 부를 것이다.

 

interface IDuck { // 1
    quack(): void;
  }
 
  class MallardDuck implements IDuck { // 3
    quack() {
      console.log('Quack!');
    }
  }
 
  class RedheadDuck { // 4
    quack() {
      console.log('q~uack!');
    }
  }
 
  function makeNoise(duck: IDuck): void { // 2
    duck.quack();
  }
 
  makeNoise(new MallardDuck()); // Quack!
  makeNoise(new RedheadDuck()); // q~uack! // 5

 

makeNoise 함수는 인터페이스 IDuck을 구현한 클래스의 인스턴스 duck을 인자로 전달받는다.

 

case1. 

makeNoise 함수에, 클래스 MallardDuck은 인터페이스 IDuck을 구현하였기 때문에 정상적으로 동작한다. 

 

case2.

 makeNoise 함수에, 클래스 RedheadDuck 에 대해서도 오류가 발생하지 않는다. 

-> 클래스 RedheadDuck은 인터페이스 IDuck을 구현하지는 않았지만 quack 메소드를 가지고 있기 때문에 에러 없이 처리된다. 

 

TypeScript는 해당 인터페이스에서 정의한 프로퍼티나 메소드를 가지고 있다면 그 인터페이스를 구현한 것으로 인정한다. 

이것을 덕 타이핑(duck typing) 또는 구조적 타이핑(structural typing)이라 한다.

 

interface IPerson {
    name: string;
  }
 
  function sayHello(person: IPerson): void {
    console.log(`Hello ${person.name}`);
  }
 
  const me = { name: 'Lee', age: 18 };
  sayHello(me); // Hello Lee

 

변수 me는 인터페이스 IPerson과 일치하지는 않는다.

하지만 IPerson의 name 프로퍼티를 가지고 있으면 인터페이스에 부합하는 것으로 인정된다.

 

인터페이스는 개발 단계에서 도움을 주기 위해 제공되는 기능으로 자바스크립트의 표준이 아니다.

따라서 위 예제의 TypeScript 파일을 자바스크립트 파일로 트랜스파일링하면 아래와 같이 인터페이스가 삭제된다.

 

"use strict";
function sayHello(person) {
    console.log(`Hello ${person.name}`);
}
const me = { name: 'Lee', age: 18 };
sayHello(me); // Hello Lee

6. 선택적 프로퍼티

 

인터페이스의 프로퍼티는 반드시 구현되어야 한다. 하지만 인터페이스의 프로퍼티가 선택적으로 필요한 경우가 있을 수 있다. 

선택적 프로퍼티(Optional Property)는 프로퍼티명 뒤에 ?를 붙이며 생략하여도 에러가 발생하지 않는다.

 

-> 원하면 구현하고 아니면 안해도 되는 느낌

->  db로 치면 not null 을 기본값으로 하는 테이블에 대해서, ? 를 쓰면  null 을 허용하는 느낌 

interface UserInfo {
    username: string;
    password: string;
    age?    : number;
    address?: string;
  }
 
  const userInfo: UserInfo = {
    username: 'ungmo2@gmail.com',
    password: '123456',
    age: 18
  }
 
  console.log(userInfo); // { username: 'ungmo2@gmail.com', password: '123456', age: 18 }

 


7. 인터페이스 상속

 

인터페이스는 extends 키워드를 사용하여 인터페이스 또는 클래스를 상속받을 수 있다.

interface Person {
    name: string;
    age?: number;
  }
 
  interface Student extends Person {
    grade: number;
  }
 
  const student: Student =  {
    name: 'Lee',
    age: 20,
    grade: 3
  }

 

복수의 인터페이스를 상속받을 수도 있다.

interface Person {
    name: string;
    age?: number;
  }
 
  interface Developer {
    skills: string[];
  }
 
  interface WebDeveloper extends Person, Developer {}
 
  const webDeveloper: WebDeveloper =  {
    name: 'Lee',
    age: 20,
    skills: ['HTML', 'CSS', 'JavaScript']
  }

 

인터페이스는 인터페이스 뿐만 아니라 클래스도 상속받을 수 있다. 단, 클래스의 모든 멤버(public, protected, private)가 상속되지만 구현까지 상속하지는 않는다.

 

class Person {
    constructor(public name: string, public age: number) {}
  }
 
  interface Developer extends Person {
    skills: string[];
  }
 
  const developer: Developer =  {
    name: 'Lee',
    age: 20,
    skills: ['HTML', 'CSS', 'JavaScript']
  }

 

 

타입스크립트에서 인터페이스는 변수나 함수 클래스의 타입을 미리 정의한다. 


Type Alias 

타입 앨리어스는 새로운 타입을 정의한다. 인터페이스와 유사하지만

타입 앨리어스는 extends 또는 implements될 수 없다. 즉, 확장이 불가능하다. 

유니온 또는 튜플을 사용해야한다면 타입 앨리어스를 사용한는 편이 유리하다.

interface Person {
    name: string,
    age?: number
  }
 
  // 빈 객체를 Person 타입으로 지정
  const person = {} as Person;
  person.name = 'Lee';
  person.age = 20;
  person.address = 'Seoul'; // Error
// 타입 앨리어스
type Person = {
    name: string,
    age?: number
  }
 
  // 빈 객체를 Person 타입으로 지정
  const person = {} as Person;
  person.name = 'Lee';
  person.age = 20;
  person.address = 'Seoul'; // Error

 

 

타입 앨리어스는 원시값, 유니온 타입, 튜플 등도 타입으로 지정할 수 있다.

// 문자열 리터럴로 타입 지정
type Str = 'Lee';

// 유니온 타입으로 타입 지정
type Union = string | null;

// 문자열 유니온 타입으로 타입 지정
type Name = 'Lee' | 'Kim';

// 숫자 리터럴 유니온 타입으로 타입 지정
type Num = 1 | 2 | 3 | 4 | 5;

// 객체 리터럴 유니온 타입으로 타입 지정
type Obj = {a: 1} | {b: 2};

// 함수 유니온 타입으로 타입 지정
type Func = (() => string) | (() => void);

// 인터페이스 유니온 타입으로 타입 지정
type Shape = Square | Rectangle | Circle;

// 튜플로 타입 지정
type Tuple = [string, boolean];
const t: Tuple = ['', '']; // Error

 

유니온 타입 

or 와 같이 여러개중 1개의 타입을 가진다. 

 

타입 가드

타입 가드는 TypeScript에서 변수의 타입을 좁히는 데 사용됩니다.

 

다음과 같은 코드에 대해서는 에러가 발생하며

 

typeof를 사용해 변수의 타입을 확인하고, 그에 따라 다른 로직 실행 가능. 

function greet(person: string | number) {
    if (typeof person == 'string'){
        console.log(person.length);
    }
}

greet('Lee') // 3