TypeScript의 타입 별칭(Type Aliases)

  • 타입 별칭(Type Aliases)은 타입의 새로운 이름을 만드는 것

    • 새로운 이름으로 기존의 타입을 참조하는 것을 의미

    • 타입 별칭을 이용하여 타입의 새로운 이름을 만들 때 키워드 type을 사용하여 작성

    • type MyString = string;
      
       let str1: string = 'hello!';
      
       // string 타입처럼 사용할 수 있습니다.
       let str2: MyString = 'hello world!';
    • 원래 string이라는 타입이 존재하고 있어, myString이라는 새로운 이름을 부여.

      • myStringstring은 동일한 의미를 갖게 됨.
      • 즉, 타입을 정의할 수 있는 모든 곳에는 타입 별칭을 쓸 수 있다.
  • 이런 방식으로 타입 별칭을 사용하면 코드를 더 간결하고 가독성 좋게 만들 수 있다.

    • 또한 복잡한 타입을 간략하게 표현하고, 타입 정의를 재사용하는 등 가독성을 높일 수 있다.

    • type Person = {
        id: number;
        name: string;
        email: string;
      }
      
       //Commentary 인터페이스에서 Person 타입을 참조하고 있습니다.
      interface Commentary {
        id: number;
        content: string;
        user: Person;
      }
      
       //객체에서 Commentary 인터페이스를 참조하고 있습니다.
      let comment1: Commentary = {
          id: 1,
          content: "뭐예요?",
          user: {
              id: 1,
              name: "김코딩",
              email: "kimcoding@codestates.com",
          },
      }
      
       //Commentary 인터페이스 내부에 content 프로퍼티가 존재하기 때문에 
      //content 프로퍼티를 작성하지 않으면 컴파일 에러가 납니다.
      let kimcoding: Commentary = {
          id: 1,
          user: {
              id: 1,
              name: "김코딩",
              email: "kimcoding@codestates.com",
          },
      };
      
       //Person 타입 내부에 isDeveloper 프로퍼티가 존재하지 않기 때문에 
      //isDeveloper 프로퍼티를 작성할 시 컴파일 에러가 납니다.
      let kimcoding: Commentary = {
          id: 1,
          content: "뭐예요?",
          user: {
              id: 1,
              name: "김코딩",
              email: "kimcoding@codestates.com",
              isDeveloper: true,
          },
      };
    • 인터페이스나 다른 변수를 정의할 때 타입 별칭으로 정의한 타입을 참조하게 됨으로써 코드를 더 간결하고 가독성 좋게 만들 수 있다.

    • 타입 별칭으로 만들어진 타입을 참조할 시에는 인터페이스와 마찬가지로 내부에 정의된 프로퍼티를 전부 참조해야만 한다.

    • 또한 타입 별칭으로 만들어진 타입 내부에 정의된 프로퍼티 외에 다른 프로퍼티를 더 작성하게 되면 그 또한 컴파일 에러가 난다.


인터페이스 vs 타입 별칭

  • 타입 별칭 또한 인터페이스와 같은 특징이 있기 때문에, 인터페이스의 역할을 타입 별칭이 수행할 수도 있다.

    • 그러나 인터페이스와 타입 별칭에는 미묘한 차이점이 있다.

    • type Person = {
          name: string;
          age: number;
      }
      
       interface User {
          name: string;
          age: number;
      }
      
       let kimcoding: Person = {
          name: '김코딩',
          age: 30,
      }
      
       let coding: User = {
          name: '김코딩',
          age: 30,
      }
  • 작성하는 동안 정의한 타입이나 인터페이스 내부에 어떤 프로퍼티가 들어가 있는지 기억이 나지 않을 경우가 있다.

  • kimcoding이 참조하고 있는 Person 타입과, coding이 참조하고 있는 User 인터페이스에 각기 마우스를 올려놓아 보았다.

    • kimcoding 객체가 참조하고 있는 Person 내부에 어떤 프로퍼티들이 정의되어 있는지 보이지만, coding 객체가 참조하고 있는 User에 내부에 어떤 프로퍼티들이 정의되어 있는지 보이지 않는다.
  • 따라서 타입 별칭으로 정의한 타입으로 작성할 시에는 조금 더 편하게 코드를 작성할 수 있게 된다.

// 이러한 차이점이 있다.

type Person = {
    name: string;
    age: number;
}

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

//에러가 발생합니다.
type Students extends Person {
    className: string;  
}

//정상적으로 동작합니다.
interface Students extends User {
	 className: string;   
}

//정상적으로 동작합니다.
interface Students extends Person {
    className: string;  
}
  • 타입 별칭은 말 그대로 타입에 새로운 이름을 부여하는 것에서 그치기 때문에 확장이 되지 않는다.
    • 그러나 인터페이스는 확장이 가능하다.
    • 인터페이스는 기존의 인터페이스 및 타입 별칭으로 만들어진 타입 둘 다 상속할 수 있기 때문에, 유연한 코드 작성을 위해서는 인터페이스로 만들어서 필요할 때마다 확장할 수 있다.



TypeScript의 타입 추론(Type Inference)

TypeScript는 정적타입을 지원하는 프로그래밍 언어

  • 정적타입 시스템을 사용하면 코드의 안정성을 높이고 디버깅을 용이하게 할 수 있다.
    • TypeScript는 타입 추론(Type Inference)이라는 기능을 통해 코드 작성을 도와준다.

타입 추론(Type Inference)의 기본

  • 타입 추론(Type Inference)은 변수나 함수의 타입을 선언하지 않아도 TypeScript가 자동으로 유추하는 기능.
    • let isNumber = 123;
    • 변수 isNumber를 선언
    • 숫자 123을 할당
      • 이 경우, 타입스크립트는 isNumber의 타입을 자동으로 숫자(Number)로 추론

최적 공통 타입 (Best common type)

  • TypeScript는 여러 표현식에서 타입 추론이 발생할 때, 해당 표현식의 타입을 사용하여 "최적 공통 타입"을 계산.
    • let x = [0, 1, null];
    • x 타입을 추론하려면 각 배열 요소의 타입을 고려해야 한다.
    • 여기서 배열의 타입으로 고를 수 있는 numbernull 있다.
    • 최적 공통 타입 알고리즘은 각 후보의 타입을 고려하여, 모든 후보의 타입을 포함할 수 있는 타입을 선택.

문맥상의 타이핑(Contextual Typing)

  • TypeScript에서 타입을 추론하는 또 하나의 방식은 바로 문맥상으로 타입을 결정하는 것.
    • 이 문맥상의 타이핑(타입 결정)은 코드의 위치(문맥)를 기준
    • function add(a, b) {
        return a + b;
      }
    • add 함수는 두 개의 매개변수를 받아 더한 값을 반환.
    • 하지만 매개변수의 타입이 명시되어 있지 않았다.
      • 이 경우, 타입스크립트는 매개변수 ab의 타입을 자동으로 추론했다.
      • 만약 매개변수 ab가 모두 숫자(Number) 타입이라면, add 함수의 반환 값도 숫자(Number) 타입으로 추론된다.

타입 추론의 장점

  1. 코드의 가독성 향상

    • 명시적으로 타입을 지정하지 않아도 코드에서 변수의 타입을 알 수 있기 때문에, 타입 추론을 사용하면 코드의 가독성이 향상.
  2. 개발 생산성 향상

    • 명시적으로 타입을 지정하지 않아도 TypeScript가 자동으로 타입을 추론하기 때문에, 타입 추론을 사용하면 코드 작성 시간을 단축할 수 있다.
  3. 오류 발견 용이성

    • TypeScript는 변수나 함수의 타입을 추론하여 타입 검사를 수행하기 때문에, 타입 추론을 사용하면 코드의 오류를 발견하는 것이 쉬워진다.

타입 추론의 단점

  1. 타입 추론이 잘못될 경우 코드 오류 발생

    • 타입 추론은 TypeScript가 자동으로 수행하는 것이기 때문에, 추론이 잘못될 경우 코드 오류가 발생할 수 있다.
  2. 명시적인 타입 지정이 필요한 경우가 있음

    • 타입 추론만으로는 부족한 경우가 있다.
    • 특히, 복잡한 함수나 객체의 경우에는 명시적인 타입 지정이 필요할 수 있다.



TypeScript의 클래스(Class)

JavaScript와 TypeScript 모두 객체 지향 프로그래밍을 지원하며 클래스(class)를 사용하여 객체를 생성할 수 있다.

그러나 두 언어의 클래스는 몇 가지 차이점이 있다.


JavaScript에서의 클래스(Class)

JavaScript에서 클래스를 사용하면 객체를 생성하고 객체의 속성과 메서드를 정의할 수 있다.

class Person {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }
  
  greet() {
    console.log(`안녕하세요, 제 이름은 ${this.name}이고, ${this.age}살 입니다.`);
  }
}
  • Person 클래스는 nameage 속성을 가지고 greet() 메서드를 정의.
  • 이 클래스를 사용하여 객체를 생성하려면 다음과 같이 할 수 있다.
    • const person = new Person('Alice', 30);
      person.greet(); // "안녕하세요, 제 이름은 Alice이고, 30살 입니다."

TypeScript에서의 클래스(Class)

TypeScript의 클래스는 JavaScript의 클래스와 비슷하지만 몇 가지 추가된 기능이 있다.

TypeScript에서는 클래스의 속성과 메서드에 대한 타입을 명시할 수 있다.

class Person {
  name: string;
  age: number;

  constructor(name: string, age: number) {
    this.name = name;
    this.age = age;
  }

  greet(): void {
    console.log(`안녕하세요, 제 이름은 ${this.name}이고, ${this.age}살 입니다.`);
  }
}
  • name 속성과 age 속성은 문자열과 숫자 타입으로 정의.

  • 약간의 차이점은 TypeScript에서 클래스를 정의할 때, constructor를 이용하여 초기화하는 멤버들은 전부 상단에서 정의를 해줘야 한다는 것.

  • 또한 contructor 내 인자로 받을 때도 정확히 타입을 명시해 줘야 한다.

    • const person = new Person('Alice', 30);
      person.greet(); // "안녕하세요, 제 이름은 Alice이고, 30살 입니다."

클래스와 상속(Inheritance)

TypeScript의 클래스(class)인터페이스(interface)와 마찬가지로 기존에 존재하던 클래스를 상속받아 확장하여 새로운 클래스를 만들 수 있다.

이때도 extends 키워드를 사용하여 상속할 수 있다.

class Animal {
    move(distanceInMeters: number): void {
        console.log(`${distanceInMeters}m 이동했습니다.`);
    }
}

class Dog extends Animal {
    speak(): void {
        console.log("멍멍!");
    }
}

const dog = new Dog();
dog.move(10);
dog.speak();
  • Animal이라는 클래스를 Dog라는 클래스가 상속받고 있다.

  • Dog 클래스는 Animal 클래스로부터 프로퍼티와 메서드를 상속받으며, Dog 클래스는 파생 클래스라고도 불리며, 하위클래스(subclasses)라고도 불린다.

  • 여기서 Animal 클래스는 기초 클래스, 상위클래스(superclasses)라고 불린다.


public, private 키워드

기본적으로 클래스 내에 선언된 멤버는 외부로 공개되는 것이 디폴트 값.

  • 그러나 공개된다고 명시적으로도 표시해 줄 수 있다.
    • 이때 public 키워드를 사용
class Person {
  public name: string;
  public age: number;

  constructor(name: string, age: number) {
    this.name = name;
    this.age = age;
  }

  greet(): void {
    console.log(`안녕하세요, 제 이름은 ${this.name}이고, ${this.age}살 입니다.`);
  }
}

// 혹은 외부에 드러내지 않을 멤버가 있다면 private 키워드로 명시해 주면 된다.
class Person {
  public name: string;
  private age: number;

  constructor(name: string, age: number) {
    this.name = name;
    this.age = age;
  }

  greet(): void {
    console.log(`안녕하세요, 제 이름은 ${this.name}이고, ${this.age}살 입니다.`);
  }
}

readonly 키워드

readonly키워드를 사용하여 프로퍼티를 읽기 전용으로 만들 수 있다.

읽기 전용 프로퍼티들은 선언 또는 생성자에서 초기화해야 한다.

class Mydog {
    readonly name: string;
    constructor(theName: string) {
        this.name = theName;
    }
}
let spooky = new Mydog("스푸키");
spooky.name = "멋진 스푸키"; // 에러
  • namereadonly로 명시되어 있기 때문에, 값을 변경할 수 없다.

  • 이런 식으로 변경되면 안 될 값을 readonly로 명시하여 보호할 수 있다.

profile
Drop the Bit!

0개의 댓글