타입을 멋지게 쓰고 싶다 🤔

houndhollis·2025년 7월 15일
4

쑈ㅔㄷ(Type)이란 뜻이다 👀

처음에는 어떻게 쓸지 고민했던 Typescript 지금은 그냥 흘러가는 대로 쓰는거 같다.

타입을 멋지게 쓰고 싶은 마음은 굴뚝같지만, 타입을 멋지게 쓴다는 것이란 뭘까?
단순히 에러만 피하는 수준이 아니라,

읽기 쉽고, 확장성 좋고, 안전하며, 선언만 봐도 의도가 명확한 타입을 설계하는 것이라고 생각한다.

그래서 한번 정리를 해볼려고 한다.

1. 타입 보단 별칭으로 작성

const createUser = (user: { name: string; age: number }) => {};
// 위 처럼 사용하면 타입의 재사용성과, 로직의 가독성이 떨어진다.

export type User = {
  name:string;
  age:number;
}

const createUser = (user:User) => {}

의미가 명확하고 재사용이 가능해진다


2. 유니언 & 리터럴 타입 적극 활용

type ButtonProps = {
  style: string;
};
// 그냥 string이면 뭐가 들어올지 알기 힘들다

type ButtonsProps = {
  style: 'primary' | 'secondary' | 'danger' | 'danger'
}

✅ 잘못된 값을 컴파일 단계에서 걸러주고, 자동완성도 더 좋아진다.


3. 매직 문자열 Enum / 상수 객체 사용

const move = (direction: 'up' | 'down' | 'left' | 'right') => {}
// 아찔하다.

const Direction = {
  UP: "up",
  DOWN: "down",
  LEFT: "left",
  RIGHT: "right",
} as const;

type Direction = (typeof Direction)[keyof typeof Direction];

const move = (direction: Direction) => {
  switch (direction) {
    case Direction.UP :
      // 생략
  }
}

✅ 값과 타입이 하나의 구조로 묶여, 확장성과 유지보수성이 좋아진다.


4. 함수 타입은 type 또는 interface로 추상화 하기

const sum: (a: number, b: number) => number = (a, b) => a + b;
// 물론 이렇게 쓴 적은 없는거 같다.

type SumFn = (a:number, b:number) => number;
const sum:SumFn = (a, b) => a + b

5. Partial / Pick / Omit 등 유틸리티 타입 적극 사용하기

type User = {
  id: string;
  name: string;
  email: string;
};

// 옵셔널로 받고 싶을 때
type EditableUser = Partial<User>;

// 특정 필드만 선택
type UserPreview = Pick<User, 'id' | 'name'>;

// 특정 필드만 제외
type UserWithoutEmail = Omit<User, 'email'>;

추가 작성

type DeepPartial<T> = {
  [P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : T[P];
};

Partial 는 1단계 속성만 optional 하게 만들지만,DeepPartial는 중첩된 객체 속성들까지 모두 optional로 만드는 것이 목적이다.


6. 조건부 타입과 제네릭 활용

type ResponseData<T> = {
  success: boolean;
  data: T;
};

const getUser = (): ResponseData<User> => ({
  success: true,
  data: { name: 'Kim', age: 30 },
});

✅ 제네릭은 타입을 재사용 가능한 함수 처럼 만들어준다.



7. ReturnType 리턴 타입을 추출

const createFormData = () => {
  return {
  	title: "",
  	content: "",
  	tags: [],
  };
}

type FormData = ReturnType<typeof createFormData>;

function validate(form: FormData) {
  if (form.title | content | tags)
  // 다 자동완성으로 된다.
}

✅ ReturnType<...>이 함수의 리턴 타입만 뽑아오는 방식이다.

마무리

type A = keyof any;
type B = keyof object;
type C = keyof Record<string, unknown>;

어떤 값이 나올까? 이런것도 재밌는거 같다 😊

이번 글에서는 간단하게 typescript를 사용하면서 느꼇던 점을 정리해봤다?

조금은 친해졌을 🗾..

profile
한 줄 소개

2개의 댓글

comment-user-thumbnail
2025년 7월 22일

실제로 중첩된 객체에 대한 옵셔널이 필요할 때가 있었는데, DeepPartial 같은 커스텀 유틸리티 타입을 정의해서 사용하는 방법이 너무 좋은 것 같습니다!!!
마지막에 문제를 통해 제가 생각하지 못했던 타입들이 나와서 다시 한번 생각해 보는 계기도 되었습니다!
좋은 글 감사합니다!

답글 달기
comment-user-thumbnail
2025년 7월 31일

타입스크립트를 바로 사용하다보니..! 세부적인 것에서 몰랐던 부분이 많았는데요! 요번 글을 통해 새로운 타입도 알게되고 원래 알던 개념도 다시 한 번 상기할 수 있는 계기가 되었습니다!! Partial, Pick, Omit 이런 타입들은 많이 안쓰게 되었던 것 같은데 적극적으로 사용해보겠습니다 ! ㅎ,ㅎ

답글 달기