[TS] 기초(3)

interface

  • 여러가지 타입을 갖는 프로퍼티를 갖는 새로운 타입 정의

  • 일반적으로 타입 체크를 위해 사용되며 변수, 함수, 클래스에 사용가능

  • 모든 메서드는 '추상 메서드' (단, abstract 키워드를 사용하지 않음)

interface User {
    [grade: number]: 'A' | 'B' | 'C' | 'D';
}

let user: User = {
    1: 'A',
    4: 'D'
}

사용이유

  • 협업하여 개발시 공통적으로 사용되는 부분에 대해 미리 정의를 함

  • 추후 코드를 합칠 때 효율적

Property

선택적 프로퍼티

?가 붙는 경우는 반드시 정의할 필요 없다.

interface User {
    userName: string;
    age?: number;
    address?: string;
}

let user: User = {
    userName: 'ddoni'
}

읽기전용 프로퍼티

readonly가 붙는 경우는 처음 정의한 후 변경이 불가능하다.

interface 확장

interface를 확장할 때 extends 예약어를 사용한다.

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

interface Developer extends Person {
    type: 'frontEnd' | 'backEnd';
}

const developer: Developer = {
    name: 'ddoni', // 상속한 프로퍼티를 꼭 정의
    type: 'frontEnd'
}

클래스에서도 interface를 상속할 수 있으며 이 경우에는 impelements 예약어를 사용해야 한다.

다중 interface 확장

콤마(,)를 사용하여 다중 인터페이스 확장이 가능하다.

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

interface Toy {
    name: string;
}

interface ToyCar extends Car, Toy {
    price: number;
}

let myCar: ToyCar = {
    color: 'red',
    wheels: 3,
    start: () => {
        console.log('start');
    },
    name: 'ddoni',
    price: 10000
}

함수 타입

인터페이스는 함수 타입도 정의해줄 수 있다.

interface SquareFunc {
    (num: number): number;
}

const squareFunc: SquareFunc = function(num: number): number {
    return num*num;
}

덕 타이핑 (Duck typing)

  • 인터페이스를 구현했다고해서 타입 체크를 통과하는 유일한 방법이 아님

  • 타입 체크에서 중요한 것은 값을 실제 가지고 있느냐

케이스 1)

  • 인터페이스에서 정의한 프로퍼티나 메소드를 가지고 있다면 그 인터페이스를 구현한 것으로 인정하며 이러한 것들을 덕 타이핑이라 한다.
interface IDuck {
    quack(): void
}

class MallardDuck implements IDuck {
    quack() {
        console.log('Quack!');
    }
}

class RedheadDuck {
    quack() {
        console.log('q~uack!');
    }
}

function makeNoise(duck: IDuck):void {
    duck.quack();
}

makeNoise(new MallardDuck());

makeNoise(new RedheadDuck()); // 오류가 나지 않는다.

케이스 2)

  • 인터페이스를 변수에 사용할 경우에도 덕 타이핑은 적용된다.
interface IPerson {
    name: string
}

function sayHello(person: IPerson): void {
    console.log(`hello ${person.name}`);
}

const me = { name: 'Lee', age: 18 };

sayHello(me); // 변수 me에는 name이라는 값이 있기 때문에 패스가 된 것

sayHello({ name: 'error', age: 19 }); // 이거는 또 오류..

Generic

타입을 지정하여 선언하기 어려운 경우에 사용하며 선언 시점이 아니라 생성 시점에 타입을 명시하여 하나의 타입만이 아닌 다양한 타입을 사용할 수 있다.

사용이유

  • 재사용성이 높은 함수와 클래스를 생성할 수 있다.

  • 오류를 쉽게 포착할 수 있다. ( any타입은 컴파일 시 타입을 체크하지 않는다 )

class Stack {
    private data: any[] = [];
    constructor() {}

    push(item: any): void {
        this.data.push(item);
    }

    pop(): any {
        return this.data.pop();
    }
}

const myStack = new Stack();
myStack.push(1);
myStack.push('a');

myStack.pop().substring(0);
myStack.pop().substring(0); // 오류 발생

위와 같은 경우 타입에 따라 새로운 클래스를 생성할수도 있지만 여러가지 타입이 필요한 경우 일일이 생성하기는 어렵기 때문에 이럴 때 Generic을 사용하면 유용하다.

class Stack<T> {
    private data: T[] = [];
    constructor() {}

    push(item: T): void {
        this.data.push(item);
    }

    pop(): T | undefined {
        return this.data.pop();
    }
}

const myStack1 = new Stack<number>(); // number 타입만
const myStack2 = new Stack<string>(); // string 타입만

함수에서의 Generic

function toPair<T, U>(a: T, b: U): [T, U] {
    return [a, b];
}

toPair<string, string>('1', '2');
toPair<string, number>('1', 2);

인터페이스에서의 Generic

interface Mobile<T> {
    name: string;
    price: number;
    option: T;
}

const m1: Mobile<object> = {
    name: 's1',
    price: 1000,
    option: {
        color: 'red'
    }
}

const m2: Mobile<string> = {
    name: 's1',
    price: 1000,
    option: 'none'
}
interface GenereicLogTextFn1 {
    <T>(text: T): T;
}

function logText<T>(text: T): T {
    return text;
}

let myString1: GenereicLogTextFn1 = logText;

interface GenereicLogTextFn2<T> {
    (text: T): T;
}

let myString2: GenereicLogTextFn2<string> = logText;

Union Type

OR 연산자와 같이 A 이거나 B이다 라는 의미의 타입을 의미한다. ( | 사용 )

function padLeft(value: string, padding: string | number) { // Union Type
  if (typeof padding === "number") {
    return Array(padding + 1).join(" ") + value;
  }
  if (typeof padding === "string") {
    return padding + value;
  }
  throw new Error(`Expected string or number, got '${typeof padding}'.`);
}
 
padLeft("Hello world", 4); // returns "    Hello world"

padding 파라미터에 any타입으로 선언하면 에러를 감지하지 못하지만 union type으로 선언한다면 string 이거나 number 타입 이외에는 에러를 감지한다.

Intersection Type

여러 타입을 모두 만족하는 하나의 타입을 의미한다. ( & 사용 )

interface ErrorHandling {
  success: boolean;
  error?: { message: string };
}
 
interface ArtistsData {
  artists: { name: string }[];
}
 
type ArtistsResponse = ArtistsData & ErrorHandling; // Intersection Type
 
const handleArtistsResponse = (response: ArtistsResponse) => {
  if (response.error) {
    console.error(response.error.message);
    return;
  }
 
  console.log(response.artists);
};

handleArtistsResponse 함수의 매개변수인 ArtistsResponse는 ArtistsData & ErrorHandling 타입에 접근 가능

네트워크 에러 핸들링 시 유용하다고 함.


Reference

좋은 웹페이지 즐겨찾기