TypeScript 문법 (열거형, 인터페이스, 타입 별칭, 타입 추론, 클래스)

잡초·2023년 5월 31일
0
post-thumbnail

열거형(Enum)

숫자형 열거형(Enum)

열거형은 숫자형과 문자열형, 혹은 이 둘의 조합으로 정의될 수 있다. 디폴트 값으로 숫자형을 사용하며, 각 값은 자동으로 0부터 시작하여 1씩 증가한다. 수동으로 값을 지정할 수도 있다.

enum Color {
  Red = 1,
  Green = 2,
  Blue = 4,
}

열거형의 값에 대해 산술 연산을 수행할 수도 있다.

enum Color {
  Red = 1,
  Green = 2,
  Blue = 4,
}

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

console.log(c);          // 출력: 2
console.log(greenValue);  // 출력: 2
console.log(blueValue);   // 출력: 4

문자형 열거형(Enum)

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

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

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

역 매핑 (Reverse mappings)

역 매핑은 숫자형 열거형에만 존재하는 특징이다. 열거형의 키(key)로 값(value)을 얻을 수 있고 값(value)으로 키(key)를 얻을 수도 있다.

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

인터페이스(Interface)

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 예약어를 사용하여 User 인터페이스를 만들었다. 인터페이스를 만들 때 예약어를 작성하고, 인터페이스의 이름을 대문자로 작성한다. 이렇게 인터페이스의 이름을 대문자로 시작하는 것은 네이밍 컨벤션이다.

네이밍 컨벤션(Naming Convention) : 이름을 짓는 일종의 관례. TypeScript로 개발할 때 대부분의 개발자는 인터페이스의 이름을 대문자로 시작하도록 작성한다. 인터페이스는 객체의 타입을 정의하고, 객체가 대문자로 시작하는 것과 유사하기 때문에 일관성 있는 코드 작성을 위해 이러한 관례를 따르는 것이다.

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

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

인터페이스와 상속

인터페이스도 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 Aliases)은 타입의 새로운 이름을 만드는 것이다. 이는 새로운 이름으로 기존의 타입을 참조하는 것을 의미한다. 타입 별칭을 이용하여 타입의 새로운 이름을 만들 때 키워드 type을 사용하여 작성한다.

type MyString = string;

let str1: string = 'hello!';

// string 타입처럼 사용할 수 있다.
let str2: MyString = 'hello world!';

인터페이스 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)

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

let isNumber = 123;

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

최적 공통 타입 (Best common type)

TypeScript는 여러 표현식에서 타입 추론이 발생할 때, 해당 표현식의 타입을 사용하여 "최적 공통 타입"을 계산한다. 아래와 같은 코드가 있다고 가정해 보자.

let x = [0, 1, null];

위 예제의 x 타입을 추론하려면 각 배열 요소의 타입을 고려해야 한다. 여기서 배열의 타입으로 고를 수 있는 두 가지 후보 number와 null가 있다. 최적 공통 타입 알고리즘은 각 후보의 타입을 고려하여, 모든 후보의 타입을 포함할 수 있는 타입을 선택한다.

문맥상의 타이핑(Contextual Typing)

타입스크립트에서 타입을 추론하는 또 하나의 방식은 바로 문맥상으로 타입을 결정하는 것이다. 이 문맥상의 타이핑(타입 결정)은 코드의 위치(문맥)를 기준으로 일어난다.

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

add 함수는 두 개의 매개변수를 받아 더한 값을 반환한다. 하지만 매개변수의 타입이 명시되어 있지 않는다. 이 경우, 타입스크립트는 매개변수 a와 b의 타입을 자동으로 추론한다. 만약 매개변수 a와 b가 모두 숫자(Number) 타입이라면, add 함수의 반환 값도 숫자(Number) 타입으로 추론된다.

타입 추론의 장단점

장점
1. 코드의 가독성 향상: 타입 추론을 사용하면 코드의 가독성이 향상된다.
2. 개발 생산성 향상: 타입 추론을 사용하면 코드 작성 시간을 단축할 수 있다.
3. 오류 발견 용이성: 타입 추론을 사용하면 코드의 오류를 발견하는 것이 쉬워진다.

단점
1. 타입 추론이 잘못될 경우 코드 오류 발생: 타입 추론은 TypeScript가 자동으로 수행하는 것이기 때문에, 추론이 잘못될 경우 코드 오류가 발생할 수 있다.
2. 명시적인 타입 지정이 필요한 경우가 있음: 타입 추론만으로는 부족한 경우가 있다. 특히, 복잡한 함수나 객체의 경우에는 명시적인 타입 지정이 필요할 수 있다.

클래스(Class)

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

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

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

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

profile
개발자가 되고싶은 잡초

0개의 댓글