Frontend Study

[FE_Bootcamp] 75일차_TypeScript(2)

킹경후 2023. 5. 31. 18:20

1. 열거형(Enum)

TypeScript의 열거형(Enum)은 특정 값의 집합을 정의할 때 사용한다. JavaScript에서는 기본적으로 열거형을 지원하지 않지만, TypeScript에서는 문자형 열거형과 숫자형 열거형을 지원한다.

 

//숫자형 열거형
enum Numbers {
  one = 1, 
  two = 2, 
  three = 3
}

const oneVal : Numbers = Number.one
const twoVal : Numbers = Numbers[1]
const threeVal : number = Numbers.three

//문자형 열거형
enum Metallica {
  vocal = 'James',
  guitar = 'Kirk',
  bass = 'Robert',
  drum = 'Lars'
}

const vocal : Metallica = Metallica.vocal
const guitarlist : Metallica = Metallica[1]
const bassits : string = Metallica.bass

 

작성 방법은 객체와 거의 비슷하다. 숫자형과 다르게 문자형 열거형은 열거형의 값을 전부 다 특정 문자 또는 다른 열거형 값으로 초기화해야 한다는 특징이 있다.

숫자형 열거형은 key로부터 value에 접근할 수 있고, 반대로 value로부터 key에 접근할 수 있다.

이를 '역매핑'이라고 한다.


2. 인터페이스

TypeScript에서 인터페이스(Interface)는 일반적으로 타입 체크를 위해 사용된다. 인터페이스는 변수, 함수, 클래스에 사용할 수 있으며, 인터페이스에 선언된 프로퍼티 또는 메서드의 구현을 강제하여 일관성을 유지하도록 한다.

 

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

// 정상 작동
const user: User = {
	name: "anna",
	age: 20
}

// 프로퍼티의 순서를 지키지 않아도 정상 작동
const user: User = {
	age: 20,
	name: "anna"
}

// 정의된 프로퍼티보다 적게 작성했기 때문에 에러
const user: User = {
	name: "anna"
}

// 정의된 프로퍼티보다 많이 작성했기 때문에 에러
const user: User = {
	name: "anna",
	age: 20,
	job: "developer"
}

 

interface 예약어를 통해 인터페이스를 생성할 수 있다. 또한 함수와 비슷하게 어떠한 프로퍼티를 조건부로 사용하고 싶다면 ? 연산자를 붙여주면 된다.

 

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

// 정상 작동
const user: User = {
	name: "anna",
	age: 20
}

// 정의된 프로퍼티보다 적게 작성했지만 ? 연산자 때문에 정상 작동
const user: User = {
	name: "anna"
}

 

또한 함수의 타입이나 클래스에서도 인터페이스를 사용할 수 있다.

 

//함수
interface User {
  (name : string, age : number) : string
}

const userInfo : User = (name, age) => {
  return `${name}은 ${age}세입니다`
}

//클래스
interface Calculator {
	add(x: number, y: number): number;
	substract(x: number, y: number): number;
}

class SimpleCalculator implements Calculator {
	add(x: number, y:number) {
		return x + y;
	}

	substract(x: number, y: number) {
		return x - y;
	}
}

const caculator = new SimpleCalculator();

 

클래스와 비슷하게 인터페이스도 상속이 가능하다

 

interface Player {
  goalkeeper : string;
  defender : string;
  midfielder : string;
  forward : string;
}

interface Team extends Player {
  coach : string;
  stadium : string;
  fans : number;
}

3. 타입 별칭

타입 별칭(Type Aliases)은 타입의 새로운 이름을 만드는 것이다. 이는 새로운 이름으로 기존의 타입을 참조하는 것을 의미한다.

 

type NewString = string
const name : NewString = 'Song'

type Person = {
  id: number;
  name: string;
  email: string;
}

interface Commentary {
  id: number;
  content: string;
  user: Person;
  //Person type 참조
}

 

이런 방식으로 타입 별칭을 사용하면 코드를 더 간결하고 가독성 좋게 만들 수 있다. 또한 복잡한 타입을 간략하게 표현하고, 타입 정의를 재사용하는 등 가독성을 높이는데도 도움이 된다.

 

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

type Students extends Person {
    className: string;  
}
//에러

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

interface Students extends Person {
    className: string;  
}
//정상 작동

 

type은 interface처럼 사용 가능하다. 다만  type은 type을 상속받을 수는 없으나 interface는 type으로부터 상속받은 프로퍼티를 가지고 새로운 interface를 만들 수 있다.


4. 타입 추론

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

 

let unknown = 'what is this?'

 

unknown은 타입이 따로 명시되어있지 않지만 값을 보고 TypeScript가 string으로 유추한다.

 

let unknownArr = [1, 2, null]

 

위 배열은 number와 null, 2가지의 타입을 가지고 있다.

이런 경우에는 최적 공통 타입 알고리즘을 통해 각 후보의 타입을 고려하여, 모든 후보의 타입을 포함할 수 있는 타입을 선택한다.

 

이런 타입 추론에는 몇 가지 장점이 있다.

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

하지만 무조건 장점만 있는 것은 아니라, 단점 역시 존재한다.

  • 타입 추론이 잘못될 경우 코드 오류 발생
  • 명시적인 타입 지정이 필요한 경우가 있음

5. TypeScrip의 클래스

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 속성은 문자열과 숫자 타입으로 정의되어 있다. TypeScript에서 클래스를 정의할 때는 constructor를 이용하여 초기화하는 멤버들은 전부 상단에서 정의를 해줘야 한다.

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

 

Class Baby extends Person {
  cry() : void {
    console.log('응애!')
  }
}

 

상속 역시 존재한다. Baby클래스는 Person클래스로부터 프로퍼티와 메서드를 상속받고 있다.

이때 Baby 클래스는 파생 클래스 또는 하위클래스(subclasses)라고 한다.

 

class Mydog {
    readonly name: string;
    constructor(theName: string) {
        this.name = theName;
    }
}
let spooky = new Mydog("스푸키");
spooky.name = "멋진 스푸키"; // 에러

 

readonly 키워드를 사용하여 프로퍼티를 읽기 전용으로 만들 수 있다. 이 읽기 전용 프로퍼티들은 선언 또는 생성자에서 초기화해주어야 한다.

위의 코드는 namereadonly로 명시되어 있기 때문에, 값을 변경할 수 없다.

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