[한입챌린지]타입스크립트 DAY8

Lina Hongbi Ko·2025년 3월 13일
0
post-thumbnail

DAY8

2025년 3월 4일

✨ 함수 오버로딩

  • 함수를 매개변수의 개수나 타입에 따라 여러가지 버전으로 정의하는 방법

    • 하나의 함수로 여러가지 버전을 정의해 범용적으로 사용할 수 있음
  • 자바스크립트에서는 안되고, 타입스크립트에서만 지원

    // 매개변수 없음
    void func() {
      printf('매개변수 없음');
    }
    
    // 매개변수 1개
    void func(int a) {
      printf(a + 20);
    }
    
    // 매개변수 2개
    void func(int a, int a) {
      printf(i + j);
    }
    
    func();
    func1(1);
    func2(1, 2);
  • 함수 오버로딩을 구현하려면 가장 먼저 버전들을 알려줘야 함

    • 함수를 정의할 때 구현부 없이 선언식만 써놓은 것 -> 오버로드 시그니처 ( {} 제외)
  • 그리고 구현할 부분을 써줌

    • 실제 구현부 -> 구현 시그니처
  • 오버로드 시그니처를 만들어두면, 실제 구현부의 매개변수의 타입들은 호출할 때 큰 영향을 미치지 않음 -> 버전을 여러개 만들어서 버전에 따라 호출하게 만들 수 있음

    /**
     * 함수 오버로딩
     * 하나의 함수를 매개변수의 개수나 타입에 따라
     * 여러가지 버전으로 만드는 문법
     * -> 하나의 함수 func
     * -> 모든 매개변수의 타입 number
     * -> Ver1. 매개변수 1개 -> 이 매개변수에 20을 곱한 값 출력
     * -> Ver2. 매개변수 3개 -> 이 매개변수들을다 더한 값을 출력
     */
    
    // 버전들 -> 오버로드 시그니처
    function func(a: number): void;
    function func(a: number, b: number, c: number): void;
    
    // 실제 구현부 -> 구현 시그니처
    function func() {}
    
    // func(); 오류
    func(1);
    // func(1, 2); 오류
    func(1, 2, 3);
  • 오버로드 시그니처의 매개변수의 개수가 다르다면, 구현 시그니처의 매개변수를 선택적 매개변수로 설정해 모든 오버로드 시그니처가 구현 시그니처에 해당할 수 있게 만들어줘야함

    // 버전들 -> 오버로드 시그니처 (함수의 구현부 없이 선언식만 써놓은 것)
    function func(a: number): void;
    function func(a: number, b: number, c: number): void;
    
    // 실제 구현부 -> 구현 시그니처
    function func(a: number, b?: number, c?: number) {
      if (typeof b === 'number' && typeof c === 'number') {
        console.log(a + b + c);
      } else {
        console.log(a * 20);
      }
    }
    
    func(1);
    func(1, 2, 3);

✨ 사용자 정의 타입 가드

  • 프로퍼티 이름을 기준으로 타입을 좁히면, 직관적으로 좋지 않고 프로퍼티가 중간에 이름이 바뀌면 이상한 타입으로 추론됨

    type Dog = {
      name: string;
      isBarked: boolean;
    }
    
    type Cat = {
      name: string;
      isScratch: boolean;
    }
    
    type Animal = Dog | Cat;
    
    function warning(animal: Animal) {
      if('isBark' in animal) {
        animal // Animal & Record<'isBark', unknown>
      } else if('isScratch' in animal) {
        animal
      }
    }
  • 이럴 때 사용하면 좋은게 -> '사용자 정의 타입 가드'

    • 보통 자바스크립트로 프로그래밍 할 때, 어떤 값이 어떤 객체에 포함 되는지 검사하는 것을 함수로 별도로 만들어서 해주는 것과 비슷
    • isDog 이라는 함수를 정의해서 타입 좁히기를 시도 -> 실패
      • 타입스크립트는 우리가 직접 만든 함수의 반환값을 가지고 타입을 좁혀주지 않음
    type Dog = {
      name: string;
      isBark: boolean;
    }
    
    type Cat = {
      name: string;
      isScratch: boolean;
    }
    
    type Animal = Dog | Cat;
    
    function isDog(animal: Animal) {
      return (animal as Dog).isBark !== undefined
    }
    
    function warning(animal: Animal) {
      if(isDog(animal)) {
        animal // animal -> 타입 좁히기 X
      } else if('isScratch' in animal) {
        // 고양이
      }
    }
  • 함수 자체를 타입 가드의 역할을 하도록 만들어줘야함

    • 형태: is 타입
      e.g) animal is Dog
      e.g) animal is Cat
    function isDog(animal: Animal): animal is Dog  {
      return (animal as Dog).isBark !== undefined
    }
    
    function isCat(animal: Animal): animal is Cat {
      return (animal as Cat).isScratch !== undefined
    }
    
    function warning(animal: Animal) {
      if(isDog(animal)) {
        animal // Dog
      } else if('isScratch' in animal) {
        animal // Cat
      }
    }

✨ 인터페이스

  • 타입에 이름을 지어주는 또 다른 문법

    // 타입 별칭
    type A = {
      a : string;
      b : number;
    }
    // 인터페이스
    interface A {
      a : string;
      b : string;
    }
  • 인터페이스 : 상호 간에 약속된 규칙

  • 객체의 구조를 정의하는데 특화된 문법

    • 상속, 합침 등의 특수한 기능을 제공
  • 타입 별칭과 문법만 다를 뿐, 기본적인 기능은 같음
    = 타입 별칭처럼 기본적으로 사용하면 됨

  • 메서드 프로퍼티의 타입을 정의할 때는 함수 표현식이나 호출 시그니처 둘 다 사용해도 됨

    • 호출 시그니처를 사용할 때는 메서드의 이름이 소괄호 앞에 붙는 다는 것 기억해야함!

      • 이전에 함수에 대해서 살펴볼 때, 객체 타입을 별칭으로 정의한 다음, 함수의 호출 시그니처를 써주면 '함수 타입을 정의' 하는 타입이 되는 것을 볼 수 있었음
      type Func = {
        (): void;
      };
    • 따라서, 메서드의 타입도 소괄호 앞에 이름을 붙여주지 않고, 호출 시그니처만 정의해주면 타입 자체를 함수 타입으로 인식 해서 sayHi와 같은 메서드 타입을 정의할 때는 이름을 붙여줘야함!!

    interface Person {
      readonly name: string;
      age?: number;
      // 메서드 타입 정의
      sayHi: () => void; // 함수 타입 표현식
      // sayHi(): void; 호출 시그니처를 사용해도 됨
      // 메서드의 이름이 소괄호 앞에 붙음!!
    }
    
    const person: Person = {
      name: '이정환',
      sayHi: function () {
        console.log('Hi');
      }
    }
    
    // person.name = '홍길동' 오류
  • 함수 오버 로딩을 메서드에 구현하고 싶을 때는 함수 타입 표현식을 쓰지 않고, 호출 시그니처를 사용해야 함!

    • 오버 로드 시그니처를 반복해서 정의할 수 있음!
    • 함수 타입 표현식을 사용하면 식별자가 중복되었다는 오류 메시지가 뜨면서 오버 로드 시그니처를 인식하지 못함
    interface Person {
      readonly name: string;
      age?: number;
      sayHi(): void;
      sayHi(a: number, b: number): void;
    }
    
    const person: Person = {
      name: '이정환',
      sayHi: function () {
        console.log('Hi');
      }
    }
    
    // person.name = '홍길동' 오류
    
    person.sayHi();
    person.sayHi(1, 2);
  • 타입 별칭과 몇 가지 차이점이 있음

    • 타입 별칭에서는 union 타입을 만들 수 있고, intersection 타입을 만들 수 있었음 -> but, 인터페이스에서는 만들 수 없음
    • 따라서, 타입별칭을 활용하거나 타입 주석을 활용해야 함
    type Type1 = number | string | Person;
    type Type2 = number & string | Person;
    
    const person: Person | number = {
      name: '이정환',
      sayHi: function () {
        console.log('Hi');
      },
    };
  • 보통 인터페이스의 이름을 정의할 때, 'I'를 붙여서 정의하기도 함 (헝가리안 표기법) / 강사님은 안붙이는 걸 개인적으로 선호한다고함 -> 팀 따라가기

✨ 인터페이스 확장하기

  • 서브타입을 보면 중복된 프로퍼티가 반복 되는 것을 볼 수 있고, 수정하기라도 한다면 하나씩 다 수정해줘야함

    interface Animal {
      name: string;
      age: number;
    }
    
    interface Dog {
      name: string;
      age: number;
      isBark: boolean;
    }
    
    interface Cat {
      name: string;
      age: number;
      isScratch: boolean;
    }
    
    interface Chicken {
      name: string;
      age: number;
      isFly: boolean;
    }
  • 이럴 때 사용하는 것 -> 인터 페이스 확장

    • extends 사용
    interface Animal {
      name: string;
      age: number;
    }
    
    interface Dog extends Animal{
      isBark: boolean;
    }
    
    const dog: Dog = {
      name: '',
      age: 1,
      isBark: true
    }
    
    interface Cat extends Animal{
      isScratch: boolean;
    }
    
    interface Chicken extends Animal{
      isFly: boolean;
    }
  • 확장 -> 상속

    • 물려받는 과정이라고 이해할 것
  • 상속을 받는 인터페이스에서 동일한 프로퍼티 타입을 다시 정의할 수 있음

    • Dog 타입의 name 프로퍼티를 string literal 타입으로 수정하면 변수 dog의 name 값이 오류가 발생하는 것을 볼 수 있음

    • 그런데, 아무 타입으로 다시 정의할 수는 없음 -> 원본 타입의 서브 타입이어야함 (Animal의 name이 string 타입이라서 Dog의 name을 '돌돌이'라고 정의 할 수 있음)

    interface Animal {
      name: string;
      age: number;
    }
    
    interface Dog extends Animal {
      name: number;
      isBark: boolean;
    }
    
    const dog: Dog = {
      name: '', // 오류 발생
      age: 1,
      isBark: true
    }
  • 인터페이스로 만든 객체 타입 말고, 타입 별칭으로 확장할 수도 있음

    
    type Animal = {
      name: string;
      age: number;
    }
    
    interface Dog extends Animal {
      isBark: boolean;
    }
  • 다중 확장

    • 여러 가지 인터페이스를 확장할 수 있음
    interface Animal {
      name: string;
      age: number;
    }
    
    interface Dog extends Animal {
      isBark: boolean;
    }
    
    interface Cat extends Animal {
      isScratch: boolean;
    }
    
    interface DogCat extends Dog, Cat {}
    
    const dogCat: DogCat = {
      name: '',
      age: 1,
      isBark: true,
      isScratch: true
    }

✨ 인터페이스 합치기

  • 선언 합침 (declaration merging)

  • 동일한 이름으로 타입 별칭을 사용하면 오류가 발생하지만, 인터페이스는 오류가 발생하지 않음

    type Person = {
      name: string;
    } // 오류 발생
    
    type Person = {
      age: number;
    } // 오류 발생
    
    interface Person2 {
      name: string;
    }
    
    interface Person2 {
      age: number;
    }
  • 동일한 이름으로 정의한 인터페이스는 결국 다 합쳐지기 때문!

    interface Person {
      name: string;
    }
    
    interface Person {
      age: number;
    }
    
    const person: Person = {
      name: '',
      age: 27
    }
    • but! 동일한 프로퍼티를 중복 정의하는데, 타입을 다르게 정의하는 경우에는 충돌이 발생함 (인터페이스의 프로퍼티의 타입을 서로 동일하게 적용해줘야함)

      • 확장한 인터페이스는 원본 타입 프로퍼티의 타입이 같지 않아도됨 (확장 !== 합침)
    interface Person {
      name: string;
    }
    
    interface Person {
      name: string;
      age: number;
    }
    
    inteface Developer extends Person {
      name: 'hello';
    }
    
    const person: Person = {
      name: '',
      age: 27
    }
  • 보통 선언 합침은 라이브러리의 타입 정의가 부실한 경우, '모듈 보강' 이라는 작업을할 때 사용함

    interface Lib {
      a: number;
      b: number;
    }
    
    interface Lib {
      c: string;
    }
    
    const lib = {
      a: 1,
      b: 2,
      c: 'hello'
    }

출처: 한 입 크기로 잘라먹는 타입스크립트

profile
프론트엔드개발자가 되고 싶어서 열심히 땅굴 파는 자

0개의 댓글