TypeScript

[TypeScript] Enum, Interface, 타입별칭, 타입추론

Summer.dev 2023. 5. 31. 15:40

열거형(Enum)

: 특정 값들의 집합

✔️ 숫자형 이넘

디폴드 값으로 숫자형을 사용하며 아래와 같이 선언한다.

enum Computer {
  Monitor = 3,
  Mouse = 7,
  Keyboard = 1,
  Speaker = 5,
}

초기 값을 주면 차례로 1씩 증가한다.

Monitor - 1
Mouse - 2
Keyboard - 3
Speaker - 4

초기 값을 주지 않으면 0부터 차례로 1씩 증가한다.

Monitor - 0
Mouse - 1
Keyboard - 2
Speaker - 3

이넘 값에 대해 산술 연산을 수행할 수도 있다.

enum Computer {
  Monitor = 3,
  Mouse = 7,
  Keyboard = 1,
  Speaker = 5,
}

let a: Computer = Computer.Keyboard;
let b: number = Computer.Mouse;

console.log(a); // 1
console.log(b); // 7

✔️ 문자형 이넘

enum Computer {
  Monitor = "MONITER",
  Mouse = "MOUSE",
  Keyboard = "KEYBOARD",
  Speaker = "SPEAKER"
}

문자형 이넘은 이넘값을 전부 다 특정 문자 또는 다른 열거형 값으로 초기화해야 한다.

숫자형 이넘과 달리 초기값을 설정하지 않았을 때 동작하는 auto-incrementing이 없지만 디버깅을 할 때  항상 명확한 값이 나와 읽기 편하다.

✔️ 역 매핑

숫자형 이넘에만 존재하는 특징이며 이넘의 키로 값을 얻을 수 있고 값으로 키를 얻을 수 있다.

enum Enum {
    A
}
let a = Enum.A;
let nameOfA = Enum[a]; // "A"

 

인터페이스(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 User {
	name: string;
	age?: number;
}

// 정상적으로 선언됩니다.
const user: User = {
	name: "anna"
}

✔️ 함수

: 인터페이스를 사용하여 함수의 매개변수와 반환 타입도 정의 할 수 있다.

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

interface Greeting {
	(user: User, greeting: string): string;
}

const greet: Greeting = (user, greeting) => {
	return `${greeting}, ${user.name}! Your job : ${user.job}.`;
}

const user: User = {
	name: "anna",
	age: 30,
	job: "developer"
};

const message = greet(user, "Hi");

console.log(message); // "Hi, anna! Your job : developer."

greet 함수Greeting 인터페이스로부터 매개변수의 타입과 반환 타입을 받으므로 명시해줄 필요가 없다.

✔️ 클래스

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();

console.log(caculator.add(4, 9)); //13
console.log(caculator.substract(10, 5)); //5

Calculator 인터페이스addsubstarct 메서드를 정의

 

SimpleCalculator 클래스Calculator 인터페이스를 사용했기 때문에

Calculator 인터페이스에 정의된 add substarct 메서드를 반드시 작성해야한다.

 

클래스를 구현할 때 인터페이스에서 정의된 함수나 메서드의 매개변수 타입과 반환 값이 일치하도록 구현해야 한다.

클래스 내부에서 해당 메서드의 매개변수 타입을 한번 더 명시 해주지 않으면 컴파일 에러가 발생한다.

✔️ 인터페이스와 상속

: extends 키워드 사용해 기존에 존재하던 클래스를 상속해 새로운 클래스를 정의할 수 있다.

인터페이스도 extends 키워드를 사용해서 기존에 존재하던 인터페이스를 상속해 확장이 가능하다.

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

interface Developer extends Person {
    language: string;
}

const person: Developer = {
    language: "TypeScript",
    age: 20,
    name: "Anna",
}

타입 별칭(Type Aliases)

: 타입의 새로운 이름을 만드는 것, type 키워드 사용

type CustomType = number;

let num: CustomType = 123;
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은 프로퍼티 타입을 보여주고 인터페이스는 안보여준다.

그러나 타입별칭은 extends 키워드를 사용한 확장이 안된다. 유연한 코드 작성을 하려면 인터페이스를 쓰는게 좋다.

타입 추론(Type Inference)

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

let isString = "KimgCoding"

TypeScript는 isString의 타입을 자동으로 문자로 추론한다.

 

최적 공통 타입(Best Common Type)

TypeScript는 여러 표현식에서 타입 추론이 발생할 때 최적 공통 타입을 계산한다.

let arr = [0, 1, null];

number, null 타입 중에 TypeScript는 최적 공통 타입 알고리즘으로 다른 타입과 가장 잘 호환되는 타입을 선택한다.

 

문맥상의 타이핑

코드의 위치를 기준으로 일어난다.

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

만약 매개변수 a,b가 둘 다 숫자 타입이면 add 함수의 반환 값도 숫자 타입으로 추론된다.

 

타입추론 장단점

장점: 가독성, 생산성, 오류발견 용이성 향상

단점: 잘못된 추론으로 오류 발생가능, 명시적인 타입 지정이 필요한 경우가 있음

 

클래스

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

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}살 입니다.`);
  }
}

constructor 를 이용하여 초기화 하는 멤버들은 상단에서 정의를 해주고

constructor 에서 인자를 받을 때도 타입을 명시해줘야 한다.

상속

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();

 

public,private

클래스 내에 선언된 멤버는 공개가 기본값이지만 public 키워드를 사용해서 명시적으로 표시할 수 있다.

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 = "멋진 스푸키"; // 에러

 

 

Reference

코드스테이츠 유어클래스

타입스크립트 핸드북