75일차

JiHun·2023년 7월 27일

부트캠프

목록 보기
52/56
post-thumbnail

💼🗂️📑📄📝

💼 열거형(Enum)

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

열거형은 기본적으로 숫자 또는 문자열 값에 대한 식별자를 정의하는 것이다. 열거형 멤버들은 내부적으로 숫자로 매핑(mapping)되어 사용된다.

enum Color {
	Red,    // 매핑: Red -> 0
  	Green,  // 매핑: Green -> 1
  	Blue,   // 매핑: Blue -> 2
}

매핑이란, 각각의 열거형 멤버가 특정한 값에 대해 연결되어 있는 것을 의미한다.

기본적으로 이러한 형태를 보여준다. 아무것도 적어서 수동으로 매핑하지 않은 경우, 자동으로 0부터 1씩 증가시켜 매핑한다. 이것을 auto-incrementing이라고 한다.

🗂️ 숫자형 열거형

디폴트 값으로 숫자를 사용한다.

enum Color {
	Red = 1,     // 매핑: Red -> 1
  	Green = 2,   // 매핑: Green -> 2
  	Blue = 4,    // 매핑: Blue -> 4
}

let c: Color = Color.Green;
let greenValue: number = Color.Green;
let blueValue: number = Color.Blue;

열거형의 값을 가지고 산술 연산 가능하다.
변수 c 처럼 열거형을 적어도 되고, 그 매핑된 값의 타입을 적어도 된다.

또한, 이걸 JavaScript에서 객체 형태로 변환하면

const Color = {
  1: "Red",
  2: "Green",
  4: "Blue",
  Red: 1,
  Green: 2,
  Blue: 4,
};

이런 객체의 형태로 변환이 될 것이다. 하지만 숫자형 열거형은 점표기법으로 접근할 수 없다. 인덱스 접근 연산자([])를 사용하여 접근한다.

console.log(Color[1])   // => "Red"
console.log(Color[2])   // => "Green"
console.log(Color[4])   // => "Blue"

Color.1을 쓰면 안 될까? 식별자 규칙을 따라서 그렇다. 식별자는 숫자로 시작할 수 없다는 규칙 때문에 TypeScript에서 인덱스 접근 연산자를 사용하도록 프로그래밍 되어 있다.

📑 역 매핑(Reverse mappings)

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

A라는 멤버를 가지는 열거형. 자동으로 0으로 매핑이 될 것이다. 따라서 변수 a는 자동으로 0이 할당된다. Enum[a]는 Enum[0]이므로 "A"라는 문자열을 반환한다.

문자형 열거형에서는 못 쓰고, 숫자형 열거형에서만 쓸 수 있다.

🗂️ 문자형 열거형

enum Direction {
	Up = "UP",
  	Down = "DOWN",
  	Left = "LEFT",
  	Right = "RIGHT",
}

let myDirection: Direction = Direction.Up;
console.log(myDirection);  // => "UP"

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


💼 인터페이스(Interface)

인터페이스(Interface)는 데이터의 형태를 정의하기 위한 추상적인 개념이다.
특정 객체가 가져야하는 속성과 메서드의 형식을 정의다.
객체가 인터페이스에서 정의한 요구 사항을 준수해야 한다는 규칙을 정의하는 것이다.
인터페이스에는 변수, 함수, 클래스를 넣을 수 있다.
예약어인 interface를 사용해서 인터페이스를 생성할 수 있다.

interface Person {
	name: string;
  	age: number;
  	sayHello: () => void;
}

const person: Person = {
	name: "홍길동",
  	age: 18,
  	sayHello: () => {
    	console.log("Hello!");
    },
}

📑 변수 인터페이스

person 객체는 Person 인터페이스가 요구하는 모든 속성과 메서드를 가지고 있기 때문에, TypeScripts는 이 객체를 Person 타입으로 인식한다.

  • 순서를 지키지 않아도 정상적으로 선언
  • 정의된 프로퍼티보다 적거나 많이 작성되면 에러

인터페이스 안의 모든 프로퍼티가 필요한 것이 아니다.
어떤 조건에서만 존재하거나 아예 없을 수 있기 때문에 ? 연산자를 사용하여 선택적 프로퍼티를 작성할 수 있다.

interface Person {
	name: string;
  	age: number;
  	sayHello?: () => void;
}

📑 함수 인터페이스

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

interface Greeting = {
	(person: Person, greeting: string): string;
}

const greet: Greeting = (person, greeting) => {
	return `${greeting}, ${person.name}!`
}

const person: Person = {
	name: "홍길동",
  	age: 18,
}

const message = greet(person, "Hello");

console.log(message); // "Hello, 홍길동!"

📑 클래스 인터페이스

클래스에서도 인터페이스를 사용할 수 있다.

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

클래스 SimpleCalculatorCalculator 인터페이스는 implement 키워드로 연결한다. 클래스 SimpleCalculatorCalculator 인터페이스에서 정의한 add, substract라는 모든 요구 사항을 구현해야 한다.

🗂️ 인터페이스와 상속

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

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

interface Developer extends Person {
	language: string;
}

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

Person 인터페이스를 상속받아 Developer 인터페이스에 상속했다. 상속을 받게 되면 Person 내부 프로퍼티를 그대로 받아 사용할 수 있다.

타입 단언(type assertion)

interface FoodStuff {
	name: string;
}

interface FoodAmount {
	amount: number;
}

interface FoodFreshness extends FoodStuff, FoodAmount {
	isFreshed: boolean;
}

const food = {} as FoodFreshness;

food.name = "egg";
food.amount = 2;
food.isFreshed: true;

상속을 받을 때는 여러 개를 받을 수 있다. (FoodStuff, FoodAmount)

타입 단언이란, TypeScript에서 컴파일러에게 "내가 이 변수의 타입을 알고 있으니, 해당 타입으로 처리를 해라"라고 명시적으로 알려주는 것이다.

💼 타입 별칭(Type Aliases)

TypeScript에서 새로운 타입 이름을 정의하는 기능이다. 즉, 기존 타입에 다른 이름을 붙여서 사용하는 것을 말한다. 타입 별칭은 복잡한 타입을 간단하게 표현할 때, 유용하며, 고드의 가독성을 향상시키고 재사용성을 높이는 데에 도움이 된다.

type Age = number; // `Age`라는 타입 별칭 정의

let age: Age;
age = 30;
type Person = {
	name: string;
  	age: number;
}

let person: Person = {
	name: "홍길동",
  	age: 18,
}

🗂️ 인터페이스 vs 타입 별칭

인터페이스를 사용하는 것보다 간결하고 쉽게 사용할 수 있다.
하지만, 더 복잡한 타입 정의나 유니온 타입, 튜플 등을 사용해야 할 때는 인터페이스가 더 적합한 경우가 있다. 인터페이스는 객체나 클래스의 형태를 더 잘 표현할 수 있으며, 구조적인 타입 검사를 보다 유연하게 수행할 수 있다.

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)

TypeScript에서 변수를 선언할 때 해당 변수의 타입을 명시적으로 지정하지 않아도 컴파일러가 자동으로 변수의 타입을 유추하는 기능을 말한다.

🗂️ 최적 공통 타입(Best common type)

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

let values = [1, 2, "three"];

모든 요소를 보고, 숫자와 문자열 모두가 될 수 있는 가장 광범위한 타입을 선택한다.
따라서, values 배열의 타입은 (number | string)[]로 결정된다.

🗂️ 문맥상의 타이핑(Contextual Typing)

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

a, b가 number 타입이면 a + b 리턴 값도 number 타입으로 추론한다.

💼 클래스(Class)

예시

class Person {
	name: string;
  	age: number;
  	
  	constructor(name: string, age: number) {
    	this.name = name;
      	this.age = age;
    }
  	
  	greet(): void {
    	console.log(`Hello, my name is ${this.name}, ${this.age} years old.`
    }
}

constructor를 이용해 초기화할 때 초기화하는 멤버들은 전부 상단에서 정의해줘야 한다.
또한, constructor에 받아서 쓸때도 정의해줘야 한다.
constructor 안 쓰면 미리 정의 안해도 된다.

🗂️ 클래스 상속(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();   // => "멍멍!"

이때, Animal 클래스는 기초 클래스, 상위 클래스
Dog 클래스는 파생 클래스, 하위 클래스라고 불린다.

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

const person = new Person("홍길동", 18);

console.log(person.name);  // => "홍길동"  // public 멤버는 클래스 외부에서 접근 가능
console.log(person.age);   // => 오류   // private 멤버는 클래스 외부에서 접근 불가

🗂️ readonly 키워드

class Me {
	readonly name: string;
  	construct(theName: string){
    	this.name = theName
    }
}

let me = new Me("홍길동");
me.name = "길동 홍";  // => 에러

readonly 키워드로 설정을 하게 되면, 외부에서 접근하면 읽을 수는 있지만, 변경을 하지는 못한다.

profile
안녕하세요. 프론트엔드 개발자 송지훈입니다.

0개의 댓글