[TypeScript] 타입 공간과 값 공간의 심벌 구분하기

ujinsim·2024년 12월 17일
0
post-thumbnail

TypeScript에서 타입과 값의 구분

  1. 타입 공간
    • 타입 공간의 심벌은 타입을 정의하고 참조할 때 사용됩니다. 이 공간에서 심벌은 타입을 의미하며, 컴파일 시점에 타입 검사를 위해 사용 number, string, boolean, User
  2. 값 공간
    • 값 공간의 심벌은 실제 런타임에 사용되는 값입니다. 이 공간의 심벌은 코드가 실행될 때 실제 메모리에서 사용 숫자 5, 문자열 "hello", 객체 { name: "Alice" }
    타입과 값의 구분 코드
// 타입 공간의 심벌
type User = {
  name: string;
  age: number;
};

// 값 공간의 심벌
const user: User = {
  name: "youjin",
  age: 21,
};

// 타입 공간의 심벌 사용 예시
function greet(user: User): string {
  return `Hello, ${user.name}`;
}

// 값 공간의 심벌 사용 예시
console.log(greet(user)); // 출력: Hello, youjin

User는 타입 공간에서 사용되며, 데이터의 구조를 정의한다

user는 값 공간에서 사용 되는 데이터이다

타입과 값의 구분 코드

// 타입 공간의 심벌
type User = {
  name: string;
  age: number;
};

// 값 공간의 심벌
const user: User = {
  name: "youjin",
  age: 21,
};

// 타입 공간의 심벌 사용 예시
function greet(user: User): string {
  return `Hello, ${user.name}`;
}

// 값 공간의 심벌 사용 예시
console.log(greet(user)); // 출력: Hello, youjin

User는 타입 공간에서 사용되며, 데이터의 구조를 정의한다

user는 값 공간에서 사용 되는 데이터이다

타입과 값의 구분을 위한 규칙

type T1 = 'string literal';
type T2 = 123;
 
const V1 = 'string literal';
const V2 = 123;

T1T2는 타입을 정의하고,

V1V2는 실제 값을 저장하는 심벌입니다

typeof 연산자의 타입 공간과 값 공간에서의 사용 차이

type T1 = typeof V1; // 타입: 'string'
type T2 = typeof V2; // 타입: 'number'

const v1 = typeof V1; // 값: "string"
const v2 = typeof V2; // 값: "number"
  • type T1 = typeof V1;는 타입 공간에서의 사용으로, V1의 타입을 참조합니다.
  • const v1 = typeof V1;는 값 공간에서의 사용으로, V1의 실제 타입을 문자열로 반환합니다.

클래스의 타입과 값 구분

  1. 타입 공간에서의 클래스

    • 타입으로서의 클래스는 해당 클래스의 인스턴스 타입을 정의합니다. 즉, 클래스를 타입처럼 사용하여 변수의 타입을 지정하거나, 제네릭 타입 제약조건으로 사용할 수 있습니다.

    타입 선언, 함수 파라미터의 타입으로 사용.

  2. 값 공간에서의 클래스

    • 값으로서의 클래스는 실제로 런타임 시점에 존재하는 생성자 함수로, 객체를 생성하거나 instanceof 연산을 통해 인스턴스인지 검사할 때 사용됩니다.

    new 키워드를 사용해 객체 생성, instanceof로 타입 검사.

class Person {
  name: string;
  constructor(name: string) {
    this.name = name;
  }

  greet() {
    console.log(`Hello, my name is ${this.name}`);
  }
}

// 타입 공간에서 사용
let personType: Person; // 타입으로 사용: Person 클래스의 인스턴스를 기대함

// 값 공간에서 사용
const personValue = new Person("Name"); // 값으로 사용: 객체 생성
personValue.greet(); // 출력: Hello, my name is Name
console.log(personValue instanceof Person); // true

타입 공간 (let personType: Person): Person은 타입으로 사용되어 personType 변수는 Person 타입의 인스턴스만 가짐

값 공간 (new Person("Name")): Person은 생성자로 사용되어 새로운 객체를 만든다

// 기본 클래스 Person 정의
class Person {
  name: string;
  constructor(name: string) {
    this.name = name;
  }

  greet() {
    console.log(`Hello, my name is ${this.name}`);
  }
}

// Person을 확장하는 Age 클래스
class Age extends Person {
  age: number;
  constructor(name: string, age: number) {
    super(name);
    this.age = age;
  }

  greetWithAge() {
    console.log(`Hello, my name is ${this.name} and I am ${this.age} years old.`);
  }
}

// 타입 공간에서의 사용 예시
let personType: Person; // Person 타입의 인스턴스를 기대
let ageType: Age; // Age 타입의 인스턴스를 기대

// personType은 Person의 인스턴스를 참조
personType = new Person("심유진");
personType.greet(); // 출력: Hello, my name is 심유진

// ageType은 Age의 인스턴스를 참조
ageType = new Age("심슨", 25);
ageType.greetWithAge(); // 출력: Hello, my name is 심슨 and I am 25 years old.

// personType에 Age 인스턴스를 할당하는 것도 가능 (Age는 Person을 확장하므로)
personType = new Age("영희", 30);
personType.greet(); // 출력: Hello, my name is 영희

// 합집합 타입 사용
type PersonOrAge = Person | Age;

let personOrAge: PersonOrAge;

// Person 타입 할당
personOrAge = new Person("Alice");
personOrAge.greet(); // 출력: Hello, my name is Alice

// Age 타입 할당
personOrAge = new Age("Bob", 40);
if (personOrAge instanceof Age) {
  personOrAge.greetWithAge(); // 출력: Hello, my name is Bob and I am 40 years old.
} else {
  personOrAge.greet(); // 해당 부분은 Age가 아닌 Person 인스턴스인 경우에만 실행
}

// 값 공간에서의 사용
const personValue = new Person("Tom"); // 값으로 사용: 객체 생성
personValue.greet(); // 출력: Hello, my name is Tom
console.log(personValue instanceof Person); // true

const ageValue = new Age("Jenny", 28); // Age 객체 생성
ageValue.greetWithAge(); // 출력: Hello, my name is Jenny and I am 28 years old.
console.log(ageValue instanceof Age); // true
console.log(ageValue instanceof Person); // true (Age는 Person을 확장했기 때문에)

두 공간(타입, 값)에서 다른 의미를 가지는 코드 패턴

1. this 키워드

  • 값으로 사용: 현재 객체를 참조
  • 타입으로 사용: 현재 클래스 타입을 참조하며, 다형성(polymorphic this)으로 사용된다
class Animal {
  name: string;
  constructor(name: string) {
    this.name = name;
  }

  // 값으로 사용: 현재 객체를 참조
  setName(name: string): this {
    this.name = name; // this는 현재 객체를 참조
    return this;
  }
}

class Dog extends Animal {
  breed: string;

  constructor(name: string, breed: string) {
    super(name);
    this.breed = breed;
  }

  // 타입으로 사용: this 타입을 반환하여 메서드 체인 
  setBreed(breed: string): this {
    this.breed = breed;
    return this;
  }
}

const myDog = new Dog("Rex", "Labrador")
  .setName("Buddy") // 메서드 체인
  .setBreed("Golden Retriever");

console.log(myDog); // Dog { name: 'Buddy', breed: 'Golden Retriever' }

//setName 메서드를 호출하면 Animal 타입이 아닌 Dog 타입의 인스턴스가 반환
//때문에 setName 메서드 호출 후에 setBreed 메서드를 호출 가능

2. &와 | 연산자

  • 값으로 사용 : 비트 연산자로 AND와 OR 연산을 수행한다
  • 타입으로 사용 : 타입 시스템에서 인터섹션(&)과 유니온(|) 타입을 정의한다
// 값으로 사용: 비트 연산
const bitwiseAnd = 5 & 3; // 0101 & 0011 = 0001 (1)
const bitwiseOr = 5 | 3;  // 0101 | 0011 = 0111 (7)
console.log(bitwiseAnd, bitwiseOr); // 출력: 1 7

// 타입으로 사용: 인터섹션(&)과 유니온(|)
type Cat = { purrs: boolean };
type Dog = { barks: boolean };
type Pet = Cat & Dog; // 인터섹션 타입: 두 타입의 모든 속성을 가짐

let myPet: Pet = { purrs: true, barks: true }; // Cat과 Dog의 속성을 모두 가짐

type AnimalType = Cat | Dog; // 유니온 타입: Cat 또는 Dog 중 하나의 속성을 가짐

let anotherPet: AnimalType = { purrs: false }; // Cat 타입으로 사용 가능

3. const

  • 값으로 사용: 변수를 선언할 때 사용하며, 값은 변경되지 xx
  • 타입으로 사용 (as const): 리터럴 값의 타입을 정확하게 고정시키거나 추론된 타입을 변경
// 값으로 사용: 변경 불가능한 변수 선언
const MAX_COUNT = 100;
// MAX_COUNT = 200; // 오류: 상수는 재할당할 수 없음

// 타입으로 사용: 리터럴 값을 고정하여 추론
const pet = {
  type: "cat",
  color: "black",
} as const; // pet의 타입이 { type: "cat"; color: "black"; }으로 고정됨

function describePet(pet: { type: "cat" | "dog"; color: string }) {
  console.log(`This ${pet.type} is ${pet.color}.`);
}

describePet(pet); // "This cat is black."

4. extends

  • 값으로 사용 (class A extends B): 클래스 상속을 통해 B 클래스의 기능을 A 클래스에 확장
  • 타입으로 사용 (interface A extends B): 인터페이스 상속을 통해 A 인터페이스가 B의 타입을 확장
  • 제네릭 타입 한정자 (Generic): 제네릭 타입에서 T가 특정 타입을 확장하도록 제한
// 값으로 사용: 클래스 상속
class Vehicle {
  speed: number = 0;
  drive() {
    console.log(`Driving at ${this.speed} km/h`);
  }
}

class Car extends Vehicle {
  setSpeed(speed: number) {
    this.speed = speed;
  }
}

const myCar = new Car();
myCar.setSpeed(60);
myCar.drive(); // 출력: Driving at 60 km/h

// 타입으로 사용: 인터페이스 상속
interface Animal {
  legs: number;
}

interface Bird extends Animal {
  canFly: boolean;
}

const sparrow: Bird = { legs: 2, canFly: true };

// 제네릭 타입 한정자로 사용
function logLength<T extends { length: number }>(item: T) {
  console.log(item.length);
}

logLength([1, 2, 3]); // 출력: 3

5. in

  • 값으로 사용 (for (key in object)): 객체의 속성을 순회할 때 사용한다
  • 타입으로 사용 (매핑된 타입): 타입 시스템에서 키의 집합을 기반으로 새로운 타입을 정의할 때 사용한다
// in 연산자의 값과 타입 공간에서의 사용

// 값 공간에서의 사용: 객체 순회
const obj = { a: 1, b: 2, c: 3 };
for (const key in obj) {
  console.log(key); // "a", "b", "c"
}

// 타입 공간에서의 사용: 매핑된 타입
type Keys = 'name' | 'age'; //'name' 또는 'age'의 문자열 리터럴 타입을 가짐
type User = { [K in Keys]: string }; // User 타입은 { name: string; age: string; }
const user: User = { name: "Alice", age: "30" };

퀴즈

  1. 다음 코드에서 타입 공간과 값 공간의 심벌을 구분해주세요
class Car {
  model: string;
  constructor(model: string) {
    this.model = model;
  }
}

type Vehicle = Car; //1

const myCar = new Car("Tesla"); //2

console.log(myCar instanceof Car); //3
  • 정답 type Vehicle = Car;:
    • 타입 공간: Vehicle은 type 키워드를 사용하여 Car 타입을 별칭으로 지정한 심벌입니다

      const myCar = new Car("Tesla");:

    • 값 공간: myCar는 실제로 메모리에 할당되는 Car 클래스의 인스턴스입니다ㅁ

      console.log(myCar instanceof Car);

    • 값 공간: instanceof는 값 공간에서만 동작하는 연산자로, myCar가 Car의 인스턴스인지 검사합니다. 여기서 Car는 값 공간에서 생성자 함수로 사용됐습니다

  1. instanceof가 타입을 검사하는지 값을 검사하는지 말해보세요
class Shape {
  sides: number;
  constructor(sides: number) {
    this.sides = sides;
  }
}

const square = new Shape(4);

console.log(square instanceof Shape); 
  • 정답

    값 공간에서의 검사:

    square instanceof Shape는 square가 Shape 클래스의 인스턴스인지 확인한다 instanceof는 square 객체의 프로토타입 체인을 검사하여 Shape의 프로토타입이 포함되어 있는지 확인함
profile
프론트엔드 공부 중인 학생입니다. 💻👩‍🎤

0개의 댓글