본문 바로가기
프로그래밍/TypeScript

TypeScript - Type Aliases, Type Inference, Class

by hustle-ing 2023. 5. 31.

Type Aliases(타입 별칭)

Type Aliases는 타입의 새로운 이름을 만드는 것이다. 타입을 정의할 수 있는 모든 곳에는 타입 별칭을 쓸 수 있다. 복잡한 타입을 간략하게 표현하고, 타입 정의를 재사용하는 등 가독성을 높일 수 있다.

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,
    },
};

Type Aliases는 인터페이스와 같은 특징이 있기 때문에, 인터페이스의 역할을 Type Aliases가 수행할 수도 있다. 하지만 인터페이스와 타입 별칭에는 미묘한 차이점이 있다.

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

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

let kimcoding: Person = {
    name: '김코딩',
    age: 30,
}

let coding: User = {
    name: '김코딩',
    age: 30,
}

VSCode로 작성 시, kimcoding 객체가 참조하고 있는 Person에 마우스를 올리면 Person 내부에 어떤 프로퍼티들이 정의되어 있는지 보인다. 그러나 coding 객체가 참조하고 있는 User에 마우스를 올리면 User 내부에 어떤 프로퍼티들이 정의되어 있는지 보이지 않는다.

VSCode에서 보이는 차이점

타입 별칭은 말 그대로 타입에 새로운 이름을 부여하는 것에서 그치기 때문에 확장이 되지 않지만, 인터페이스는 확장이 가능하다.

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;  
}

 

 

 

Type Inference(타입 추론)

Type Inference는 변수나 함수의 타입을 선언하지 않아도 TypeScript가 자동으로 유추하는 기능이다.

let isNumber = 123;

이 경우, 타입스크립트는 inNumber의 타입을 자동으로 숫자로 추론한다.

 

여러 표현식에서 타입 추론이 발생할 때, 해당 표현식의 타입을 사용하여 "최적 공통 타입"을 계산한다.

let x = [0, 1, null];

여기서 배열의 타입으로 고를 수 있는 두 가지 후보는 number와 null이다. 최적 공통 타입 알고리즘은 각 후보의 타입을 고려하여, 모든 후보의 타입을 포함할 수 있는 타입을 선택한다.

 

타입 스크립트에서 타입을 추론하는 또 하나의 방식은 바로 문맥상으로 타입을 결정하는 것이다.

function add(a, b) {
  return a + b;
}

이 경우, 타입스크립트는 매개변수 a와 b의 타입을 자동으로 추론했다. 만약 매개변수 a와 b가 모두 숫자(Number) 타입이라면, add 함수의 반환 값도 숫자(Number) 타입으로 추론된다.

 

타입 추론의 장점

  • 코드의 가독성 향상
  • 개발 생산성 향상
  • 오류 발견 용이성

타입 추론의 단점

  • 타입 추론이 잘못된 경우 코드 오류 발생
  • 명시적인 타입 지정이 필요한 경우가 있다. 특히, 복잡한 함수나 객체의 경우에는 명시적인 타입 지정이 필요할 수 있다.

 

 

 

 

TypeScript에서의 Class

JavaSciprt에서의 Class

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

위 코드에서 Person 클래스는 name과 age 속성을 가지고 greet( ) 메서드를 정의한다. 이 클래스를 사용하여 객체를 생성하려면 다음과 같다.

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

 

TypeScript에서의 Class

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

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 속성은 문자열과 숫자 타입으로 정의되어 있다. 차이점은 constructor를 이용하여 초기화하는 멤버들을 전부 상단에서 정의를 해줘야 한다는 것이다. 또한 constructor 내 인자로 받을 때도 정확히 타입을 명시해줘야 한다.

 

Class와 Inheritance(상속)

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); // "10m 이동했습니다."
dog.speak(); // "멍멍!"

 

public, private

기본적으로 클래스 내에 선언된 멤버는 외부로 공개되는 것이 디폴트 값이다. 그러나 공개된다고 명시적으로 표시해 줄 수도 있다. 이 때 public 키워드를 사용한다. 혹시 외부에 드러내지 않을 멤버가 있다면 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 = "멋진 스푸키"; // 에러

 

댓글