TypeScript 기본기 (2) Enum, Interface, 그 밖의 타입들

LeeKyungwon·2024년 5월 17일
0

프론트엔드 

목록 보기
35/56
post-custom-banner

Enum (열거형)

상품 사이즈 처럼 값의 종류를 나열할 수 있는 타입

enum Size {
  S, 
  M,
  L,
  XL,
}

let product: {
  id: string;
  name: string;
  price: number;
  membersOnly?: boolean;
  sizes: Size[];
} = {
  id: 'c001',
  name: '코드잇 블랙 후디',
  price: 129000,
  sizes: [Size.M, Size.L],
};

실제로는 0부터 시작하는 정수이다.
if문 같은 것을 쓸 때 0이 false 처리될 수 있으므로 값을 정해놓고 쓰는 것이 좋다.
=을 쓰면 값을 정할 수 있다.

enum Size {
  S='S', 
  M='M',
  L='L',
  XL='XL',
}

let product: {
  id: string;
  name: string;
  price: number;
  membersOnly?: boolean;
  sizes: Size[];
} = {
  id: 'c001',
  name: '코드잇 블랙 후디',
  price: 129000,
  sizes: [Size.M, Size.L],
};

Interface

똑같은 타입을 여러번 정의하는 것은 유지보수하기에 좋은 코드는 아니다. 이럴 때 Interface를 사용한다.

interface Product {
  id: string;
  name: string;
  price: number;
  membersOnly?: boolean;
}

const product1: Product = {
  id: 'c001',
  name: '코드잇 블랙 후디',
  price: 129000,
};
const product2: Product = {
  id: 'c002',
  name: '코드잇 그레이 후디',
  price: 179000,
};

상속이 가능하다. (extends 뒤에 부모 Interface를 적어주면 된다.)

interface ClothingProduct extends Product {
  size: Size[];
}

const product1: ClothingProduct = {
  id: 'c001',
  name: '코드잇 블랙 후디',
  price: 129000,
};

//함수의 타입도 정의할 수 있다.
interface PrintProductFunction {
  (product: Product): void;
}

리터럴 타입

타입스크립트에서는 변수를 지정할 때 let을 쓰느냐, const를 쓰느냐에 따라서 타입이 다르게 지정된다.

let productName1 = '블랙 티셔츠';//string
const productName2 = '텀블러';//문자열이 타입으로 들어가 있다.

리터럴 : 변수의 값을 곧 타입으로, const는 값이 바뀔 일이 없으니까 아예 변수의 값을 타입으로 추론한 것이다.
문자 리터럴 타입은 문자형 타입에 포함되고 숫자 리터럴 타입은 숫자형 타입에 포함된다.

숫자형 => 숫자 리터럴 타입 인정 불가
숫자 리터럴 => 숫자형 타입 인정 가능

타입 별칭

타입도 변수처럼 이름을 붙여줄 수 있다.
모든 타입에 사용할 수 있다.(함수나 객체에도 가능)
객체에서는 타입 별칭도 가능하지만 interface를 사용하는 것이 더 좋다.

type Cart = string[];

type CartResultCallback = (result: boolean) => void;

interface Product = {
  id: string;
  name: string;
}

const cart: Cart = [
  'c001',
  'c001',
  'c002',
];

interface User {
  username: string;
  email: string;
  cart: Cart;
}

const user: User = {
  username: 'codeit',
  email: 'typescript@codeit.kr',
  cart,
}

Union 타입

자바스크립트의 OR 연산자처럼 A이거나 B이다. 라는 의미의 타입이다.
|를 사용해서 나타낸다.
만약 공통된 타입이 아닌 특정 타입만 처리하도록 하려면 if in문을 사용해서 처리한다.

enum ClothingSize {
  S = 'S',
  M = 'M',
  L = 'L',
  XL = 'XL',
}

interface Product {
  id: string;
  name: string;
  price: number;
  membersOnly?: boolean;
}

interface ClothingProduct extends Product {
  sizes: ClothingSize[];
  color: string;
}

interface ShoeProduct extends Product {
  sizes: number[];
  handmade: boolean;
}

function printSizes(product: ClothingProduct | ShoeProduct) {
  const availableSizes = product.sizes.join(', ');
  console.log(`구매 가능한 사이즈는 다음과 같습니다: ${availableSizes}`); //공통된 프로퍼티만 참조할 수 있다.
  
  if ('color' in product) {
    console.log(`색상: ${product.color}`};
  } //특정 타입만 처리하도록 할 수 있다.
}

const product1: ClothingProduct = {
  id: 'c001',
  name: '코드잇 블랙 후드 집업',
  price: 129000,
  membersOnly: true,
  sizes: [ClothingSize.M, ClothingSize.L],
  color: 'black',
};

const product2: ShoeProduct = {
  id: 's001',
  name: '코드잇 스니커즈',
  price: 69000,
  membersOnly: false,
  sizes: [220, 230, 240, 260, 280],
  handmade: false,
};

printSizes(product1);
printSizes(product2);

Intersection 타입

type을 합치는 것
A와 B의 성질을 모두 갖는 타입을 만들고 싶을 때
&도 |와 마찬가지로 여러번 쓸 수 있다

interface Id { 
  id: string;
}
interface Timestamp {
  createdAt: Date;
  updatedAt: Date;
}

type Product = Id & {
  name: string;
  price: number;
  membersOnly?: boolean;
}

type User = Id & Timestamp &{
  username: string;
  email: string;
}

interface Review = Id & Timestamp &{
  productId: string;
  userId: string;
  content: string;
}

const product: Product = {
  id: 'c001',
  name: '코드잇 블랙 후드티',
  price: 129000,
}

const user: User = {
  id: 'user0001',
  username: 'codeit',
  email: 'typescript@codeit.kr',
  createdAt: new Date(),
  updatedAt: new Date(),
}

const review: Review = {
  id: 'review001',
  userId: user.id,
  productId: product.id,
  content: '아주 좋음',
  createdAt: new Date(),
  updatedAt: new Date(),
}

Union 타입

Intersection 타입

keyof, typeof 연산자

interface Product {
  id: string;
  name: string;
  price: number;
  membersOnly?: boolean;
}

type ProductProperty = keyof Product;//Product 타입에 변화가 생겨도 매번 바꿔주지 않아도 된다.
const productTableKeys: ProductProperty[] = ['name', 'price', 'membersOnly'];

const productTableKeys: (keyof Product)[] = ['name', 'price', 'membersOnly'];//이렇게 바로 사용할 수도 있다

const product: Product = {
  id: 'c001',
  name: '코드잇 블랙 후드 집업',
  price: 129000,
  membersOnly: true,
};

for (let key of productTableKeys) {
  console.log(`${key} | ${product[key]}`);
}

let product2 : typeof product; // 결과가 타입스크팁크 타입으로 나온다.
console.log(typeof product); //자바스크립트 결과이므로 결과가 문자열이 된다.

keyof

객체 타입에서 프로퍼티 이름들을 모아서 Union한 타입으로 만들고 싶을 때 사용

interface Product {
  id: string;
  name: string;
  price: number;
  membersOnly?: boolean;
}

type ProductProperty = keyof Product; // 'id' | 'name' | 'price' | 'membersOnly';

typeof

typeof 이미 존재하는 타입을 가져와서 타입을 정리할때 사용한다.
자바스크립트 코드에서 사용하면 결괏값이 문자열이지만, 타입스크립트 코드에서 쓸 때는 결과 값은 타입스크립트의 타입

const product: Product = {
  id: 'c001',
  name: '코드잇 블랙 후드 집업',
  price: 129000,
  salePrice: 98000,
  membersOnly: true,
};

console.log(typeof product); // 문자열 'object'

const product2: typeof product = { // 타입스크립트의 Product 타입
  id: 'g001',
  name: '코드잇 텀블러',
  price: 25000,
  salePrice: 19000,
  membersOnly: false,
};

타입 별칭을 쓰면 좋은 경우

Enum과 타입 별칭

아래 두 코드는 거의 같은 역할을 하는 코드이다.

Enum을 사용한 경우 (권장)

enum UserType {
  Admin = 'admin',
  User = 'user',
  Guest = 'guest',
}

const role = UserType.Admin;
console.log(role === UserType.Guest);

타입 별칭과 Union을 사용한 경우

type UserType = 'admin' | 'user' | 'guest'

const role: UserType = 'admin';
console.log(role === 'guest');

두 코드의 차이는 다음과 같다.

JavaScript로 트랜스파일링 했을 때

우선 Enum 코드를 자바스크립트로 트랜스파일링해 보면 다음과 같다.

"use strict";
var UserType;
(function (UserType) {
    UserType["Admin"] = "admin";
    UserType["User"] = "user";
    UserType["Guest"] = "guest";
})(UserType || (UserType = {}));
const role = UserType.Admin;
console.log(role === UserType.Guest);

Enum은 별도의 자바스크립트 객체를 만들어서 그 객체를 사용한다. UserType은 자바스크립트에서 아래와 같은 객체이다.

{ Admin: 'admin', User: 'user', Guest: 'guest' }

예를 들어서 가능한 UserType 값들을 모두 활용해서 어떤 동작을 구현하고 싶다면 Enum을 써서 Object.keys()라는 함수를 사용해 볼 수 있다.

console.log(Object.keys(UserType)); // ['Admin', 'User', 'Guest']

반면에 타입 별칭은 타입스크립트에서만 의미 있는 코드이다. 그래서 Enum과 달리 자바스크립트로 트랜스파일 했을 때 추가로 객체 같은 걸 만들지 않고 단순히 값만 사용하는 코드가 만들어진다.

"use strict";
const role = 'admin';
console.log(role === 'guest');

어떤 걸 써야 할까?

대부분의 경우 Enum 또는 타입 별칭을 모두 사용할 수 있지만 되도록 Enum의 목적에 맞는 경우라면 Enum 문법을 사용하는 걸 권장

Interface와 타입 별칭

아래 두 코드는 거의 같은 역할을 하는 코드이다.

Interface를 사용한 경우 (권장)

interface Entity {
  id: string;
  createdAt: Date;
  updatedAt: Date;
}

interface User extends Entity {
  username: string;
  email: string;
}

타입 별칭을 사용한 경우

type Entity = {
  id: string;
  createdAt: Date;
  updatedAt: Date;
}

type User = Entity & {
  username: string;
  email: string;
}

Interface의 상속과 Intersection의 가장 큰 차이점은 Intersection은 두 가지 이상의 타입을 한 번에 합칠 수 있다는 것인데, 이것도 Interface로 아주 불가능하지는 않다.

interface Entity {
  id: string;
}

interface TimestampEntity extends Entity {
  createdAt: Date;
  updatedAt: Date;
}

interface User extends TimestampEntity {
  username: string;
  email: string;
}

type Id = {
  id: string;
}

type Entity = {
  createdAt: Date;
  updatedAt: Date;
}

type User = Id & Entity & {
  username: string;
  email: string;
}

어떤 걸 써야 할까?

대부분의 경우 Interface와 타입 별칭을 둘 다 사용할 수 있다. 하지만 되도록 Interface의 목적에 맞는 경우라면 Interface를 사용하는 걸 권장

타입 별칭은 언제 쓰면 좋을까?

타입 별칭은 타입에 '이름'을 정하는 문법이다. 복잡한 타입을 만들고, 그 타입을 여러 곳에서 활용할 때 사용하면 된다. 예를 들자면 아래처럼 복잡한 타입을 만들고 여러 곳에서 재활용할 수 있다.

type Point = [number, number];
type SearchQuery = string | string[];
type Result = SuccessResult | FailedResult;
type Coupon = 
  | PromotionCoupon
  | EmployeeCoupon
  | WelcomCoupon
  | RewardCoupon
  ;
post-custom-banner

0개의 댓글