Programmers

[43일차]타입스크립트 2일차: 타입과 객체 지향 프로그래밍의 통합.

PARKpatchnotes 2025. 11. 11. 16:26

1. 리터럴 타입 (Literal Types)

리터럴 타입은 특정 값 자체를 타입으로 사용하는 것이다. 이는 변수에 할당될 수 있는 값을 정확히 지정하여, 코드의 안정성을 높이는 데 사용된다.

문자열 리터럴

지정된 문자열 값만 허용하는 타입이다.

let userRole: 'admin' | 'user';
userRole = 'admin'; // o

userRole = 'guest'; // error: 'admin' 또는 'user'만 할당 가능하다.

숫자 리터럴

지정된 숫자 값만 허용하는 타입이다.

let httpStatusCode: 200 | 404 | 500;
httpStatusCode = 404; // o

httpStatusCode = 301; // error: 200, 404, 500만 할당 가능하다.

Boolean 리터럴

true 또는 false 중 하나만 허용하도록 지정하는 타입이다.

let isPermanent: true;
isPermanent = true; // o

isPermanent = false; // error: true만 할당 가능하다.

객체 리터럴

객체의 구조를 값으로 사용하여 타입을 정의한다. 해당 구조에 포함된 모든 속성이 존재해야 한다.

let itemInfo: { id: number; name: string; inStock: boolean };

itemInfo = {
  id: 1,
  name: "MacBook Pro",
  // error: 'inStock' 속성이 없으므로 에러가 발생한다.
};

itemInfo = {
    id: 2,
    name: "iPad",
    inStock: "yes", // error: inStock은 boolean 타입이어야 한다.
}

2. 타입 별칭 (Type Alias)

type 키워드를 사용하면 복잡한 타입을 간단한 이름으로 재사용할 수 있다. 특히 리터럴 타입이나 유니온 타입을 조합하여 만든 타입을 재사용할 때 매우 유용하다.

type UserRole = 'admin' | 'user' | 'guest';

let roleA: UserRole = 'admin';  // o
let roleB: UserRole = 'manager'; // error: 'admin', 'user', 'guest' 중 하나여야 한다.

type Product = {
    id: number;
    name: string;
};

let productA: Product = { id: 100, name: "Keyboard" }; // o

3. any 타입

any 타입은 이름 그대로 어떤 타입의 값이든 할당할 수 있게 해주는 타입이다. 즉, any로 선언된 변수는 타입스크립트의 타입 검사 규칙을 따르지 않는다. 이는 자바스크립트처럼 자유롭게 코드를 작성할 수 있게 해주지만, 타입스크립트의 가장 큰 장점인 타입 안정성을 포기하는 것과 같다. 따라서 any 타입의 남용은 피해야 하며, 외부 라이브러리의 타입을 알 수 없는 경우 등 불가피한 상황에서만 제한적으로 사용하는 것이 권장된다.

let flexibleVar: any;

flexibleVar = 100;          // o
flexibleVar = 'Hello';      // o
flexibleVar = { a: 1 };     // o
flexibleVar.nonExistentMethod(); // 런타임 에러가 발생할 수 있다.

4. 유니온 타입 (Union Type)

유니온 타입은 | 기호를 사용하여 여러 개의 타입을 하나로 결합하는 것이다. 이를 통해 하나의 변수가 여러 타입의 값을 가질 수 있도록 허용할 수 있다. 리터럴 타입이 값의 범위를 제한했다면, 유니온 타입은 타입의 범위를 제한한다.

let identifier: number | string;

identifier = 12345;      // o
identifier = 'user-abc'; // o
identifier = true;       // error: number 또는 string 타입만 가능하다.

타입 가드 (Type Guard)

유니온 타입을 사용할 때, 현재 변수의 타입이 무엇인지 확인하고 그에 맞는 안전한 코드를 작성해야 한다. 이처럼 특정 타입임을 보장하는 로직을 타입 가드라고 한다.

  • typeof : 원시 타입(string, number, boolean 등)을 확인할 때 사용한다.
  • instanceof : 클래스의 인스턴스인지 확인할 때 사용한다.
  • in : 객체에 특정 속성이 존재하는지 확인할 때 사용한다.
  • Array.isArray() : 배열인지 확인할 때 사용한다.
  • 속성 직접 접근: 객체의 고유한 속성을 확인하여 타입을 구분할 때 사용한다.

5. 배열과 튜플 타입

배열 타입 (Array)

배열은 동일한 타입의 요소들로 구성된 자료구조이다.

// 숫자만 포함하는 배열
let fibonacci: number[] = [1, 1, 2, 3, 5, 8];

// 문자열만 포함하는 배열
let fruits: string[] = ['Apple', 'Banana', 'Cherry'];

// 여러 타입을 포함하는 배열 (유니온 타입 활용)
let mixedArray: (number | string)[] = [1, 'Apple', 2, 'Banana'];

// 읽기 전용 배열 (수정 불가)
const readOnlyNumbers: ReadonlyArray<number> = [1, 2, 3];
readOnlyNumbers.push(4); // error: 'push' 속성이 'readonly number[]' 형식에 없다.
readOnlyNumbers[0] = 0; // error: 읽기 전용 속성이므로 수정할 수 없다.

튜플 타입 (Tuple)

튜플은 길이가 고정되고 각 요소의 타입이 정해져 있는 배열이다. 순서와 타입이 모두 일치해야 한다.

let userInfo: [string, number, boolean];
userInfo = ['JHParrrk', 30, true]; // o
userInfo = [30, 'JHParrrk', true]; // error: 타입의 순서가 맞지 않는다.
userInfo = ['JHParrrk', 30];      // error: 요소의 개수가 맞지 않는다.

🍯 Tip!

함수의 매개변수에서 선택적 매개변수(?)는 항상 필수 매개변수 뒤에 위치해야 한다.

// error: 필수 매개변수 'c'는 선택적 매개변수 'b' 뒤에 올 수 없다.
function func(a: string, b?: number, c: string) {}

// 올바른 사용법
function func(a: string, c: string, b?: number) {}

또한, 선택적 매개변수는 (타입 | undefined)로 취급되므로, 함수 내부에서 undefined일 가능성을 항상 고려해야 한다.


6. 클래스 (Class)

클래스는 객체 지향 프로그래밍의 핵심 요소로, 속성과 메서드를 포함하는 객체를 생성하기 위한 템플릿이다.

기본 클래스와 접근 지정자

private, protected, public과 같은 접근 지정자를 사용하여 클래스 멤버의 접근 범위를 제어할 수 있다. private으로 선언된 멤버는 클래스 외부에서 접근할 수 없다.

type UserRole = "admin" | "user";

class User {
  private _username: string;
  private _age: number;
  private _role?: UserRole;

  constructor(username: string, age: number, role?: UserRole) {
    this._username = username;
    this._age = age;
    this._role = role;
  }

  public getUsername(): string {
    return this._username;
  }
}

const userA = new User("JHParrrk", 30, "admin");
console.log(userA.getUsername()); // "JHParrrk"

console.log(userA._age); // error: '_age' 속성은 private이며 'User' 클래스 내에서만 접근할 수 있다.

생성자 매개변수 속성

타입스크립트에서는 생성자의 매개변수에 접근 지정자를 붙여 코드를 간결하게 작성할 수 있다. 이렇게 하면 해당 매개변수의 이름으로 클래스 속성이 자동으로 선언되고 초기화된다.

type UserRole = "admin" | "user";

class User {
  constructor(
    private _username: string,
    private _age: number,
    public readonly role?: UserRole // readonly 속성도 가능
  ) {}

  public getUsername(): string {
    return this._username;
  }
}

const userB = new User("JHParrrk", 30, "admin");
console.log(userB.getUsername()); // "JHParrrk"

Getters & Setters

private 멤버에 대한 접근을 제어하면서도 외부에서 값을 읽거나 수정할 수 있도록 getset 접근자를 사용할 수 있다. 이를 통해 속성에 접근하는 것처럼 자연스럽게 메서드를 호출할 수 있다!

class User {
  constructor(private _username: string, private _age: number) {}

  get username(): string {
    console.log("Getting username...");
    return this._username;
  }

  set age(newAge: number) {
    if (newAge > 0) {
      this._age = newAge;
    } else {
      console.error("Age must be positive.");
    }
  }

  get age(): number {
    return this._age;
  }
}

const userC = new User("JHParrrk", 30);

// Setter 호출
userC.age = 31;

// Getter 호출
console.log(userC.username); // "Getting username..." followed by "JHParrrk"
console.log(userC.age);      // 31