[우아한 타입스크립트(with 리액트)] - 2장. 타입 (2.3 ~ 2.4)

Rachel·2024년 3월 27일
0

우아한형제들 웹프론트엔드개발그룹, 『우아한 타입스크립트 with 리액트』, 한빛미디어(2023) p.66 ~ 78 참고

원시타입

1. boolean

2. undefined

3. null

let value: null | undefined;
console.log(value); // undefined (값이 아직 할당되지 않음)

value = null;
console.log(value); // null
  • null: 명시적, 의도적으로 값이 아직 비어있을 수 있음을 보여준다.
  • undefined: 값이 할당 되지 않음

🚨 자바스크립트에서는 흔히 값이 없다는 것을 나타낼 때 위 둘을 혼용하고, 동등 연산자(==)로는 둘이 동일하다고 나온다 😱 -> 항상 ===을 더 권장해서 써야하는 이유 + 1

console.log(null == undefined); // true
type Person1 = {
  name: string;
  job?: string;
};

type Person2 = {
  name: string;
  job: string | null;
};

Person1에서는 job 속성 (직업)이 있을 수도 또는 없을 수도 있음을 나타냄.

Person2에서는 job이라는 속성을 사람마다 갖고 있지만 값이 비어있을 수도 있다는 것을 나타낸다. (무직인 상태)

4. number

자바스크립트의 숫자에 해당하는 모든 원시 값을 할당 가능(+ NaN, Infinity ...)

5. bigInt

const bigNumber1: bigInt = BigInt(999999999999);
const bigNumber1: bigInt = 999999999999n;

ES2020에서 새롭게 도입된 데이터 타입으로 타입스크립트 3.2 버전부터 사용 가능.

  • 자바스크립트에서는 가장 큰 수 Number.MAX_SAFE_INTEGER(2**53-1)를 넘어가는 값을 처리할 수 없었는데 bigInt를 사용하면 이보다 큰 수를 처리할 수 있다.
  • number과 bigInt는 엄연히 서로 다른 타입이기 때문에 상호 작용은 불가능.

6. string

문자열. 공백도 해당. ``(백틱)으로 감싼 문자열 내부에 변숫값 포함 -> 템플릿 리터럴 문법

7. symbol

const MOVIE_TITLE = Symbol("title");
const MUSIC_TITLE = Symbol("title");

console.log(MOVIE_TITLE === MUSIC_TITLE); // false

let SYMBOL: unique symbol = Symbol(); // A variable whose type is a 'unique symbol' type must be 'const'

ES2015에서 도입된 데이터 타입으로 Symbol() 함수를 사용하면 어떤 값과도 중복되지 않는 유일한 값을 생성할 수 있다.

  • 타입스크립트에서는 symbol 타입과 const 선언에서만 사용할 수 있는 unique symbol 타입이라는 symbol의 하위 타입도 있다.

ts-config의 strictNullChecks 옵션을 활성화했을 때는 사용자가 명시적으로 해당 타입에 null이나 undefined를 포함해야만 null과 undefined를 사용할 수 있다.
그렇지 않으면 null, undefined가 될 수 있는 경우에 타입스크립트 에러가 발생하는데 보통 타입 가드(권장)로 null, undefined가 되는 경우를 걸러낸다.


객체 타입

앞에서 언급한 7가지 원시 타입에 속하지 않는 값은 모두 객체 타입으로 분류할 수 있다.
타입스크립트에서는 다양한 형태를 가지는 객체마다 개별적으로 타입을 지정할 수 있다.

object

가급적 사용 권장 x -> any 타입과 유사하게 객체에 해당하는 모든 타입 값을 유동적으로 할당할 수 있어 정적 타이핑의 의미가 크게 퇴색된다.

function isObject(value: object) {
    return (
        Object.prototype.toString.call(value).replace(\/[|\]|\s|object/g, "") === "Object"
    );
}

// 객체, 배열, 정규 표현식, 함수, 클래스 등 모두 object 타입과 호환됨
isObject({});
isObject({name: "KG"});
isObject([0, 1, 2]);
isObject(new RegExp("object"));
isObject(function () {
    console.log("hello world");
});

// 그러나 원시타입은 호환되지 않는다.
isObject(20); // false
isObject("KG"); // false

{}

객체 리터럴 방식으로 객체를 생성할 때 사용.
타입스크립트에서는 객체를 타이핑할 때도 중괄호를 쓸 수 있는데, 중괄호 안에 객체의 속성 타입을 지정해주는 식으로 사용
= 타이핑되는 객체가 중괄호 안에서 선언된 구조와 일치해야 한다는 것을 의미

const noticePopup: {title: string; description: string} = {
    title: 'IE 지원 종료 안내',
    description: '2022.07.15일부로 배민상회 IE 브라우저 지원을 종료합니다.';
}

타입스크립트에서는 {}로 명시하면 빈 객체임을 의미해 어떤 값도 속성으로 할당할 수 없다.
빈 객체 타입 지정을 위해서는 유틸리티 타입으로 Record<string, nev-er>처럼 사용하는게 바람직.
{} 타입으로 지정된 객체는 완전히 비어있는 순수한 객체를 의미하는 것이 x.
자바스크립트 프로토타입 체이닝으로 Object 객체 래퍼에서 제공하는 속성에는 정상적으로 접근할 수 있다.

array

자바스크립트에서는 하나의 배열 안에 여러 타입 값이 혼재될 수 있지만 타입스크립트에서는 배열을 array라는 별도 타입으로 다루고 배열 타입은 하나의 타입 값만 가질 수 있다.
Array 키워드로 선언하거나 대괄호([])를 사용해 선언한다 -> 팀 컨벤션에 맞게 사용

const getCartList = async (cartId: number[]) => {
  const res = await CartApi.GET_CART_LIST(cartId);
  return res.getData();
};

getCartList([]); // 빈 배열도 가능
getCartList([1001]);
getCartList([1001, 1002, 1003]); // 타입 원소 몇 개가 들어와도 상관 x
getCartList([1001, "1002"]); // (X)

튜플 타입도 대괄호로 선언되므로 주의
튜플의 대괄호 내부에는 선언 시점에 지정해준 타입 값만 할당 가능하고 원소 개수도 타입 선언 시점에 미리 정해진다.
-> 객체 리터럴에서 선언하지 않은 속성을 할당하거나, 선언한 속성을 할당하지 않을 때 에러가 발생하는 점과 비슷하다.

🌟 type과 interface 키워드

object 타입은 실무에서 사용 잘 x.
객체를 타이핑하기 위해서는 타입스크립트에서만 독자적으로 사용할 수 있는 키워드를 사용하는게 일반적.

중괄호를 사용한 객체 리터럴 방식으로 타입을 매번 일일이 지정하기에는 중복적인 요소가 많다.

type NoticePopupType = {
    title: string;
    description: string;
}

const noticePopup1: NoticePopupType = {...};

타입스크립트 컴파일러가 변수 사용 방식과 할당된 값의 타입을 분석해서 타입을 유추한다.
-> 타입 추론은 개인의 취향 또는 팀 컨벤션에 따라 다를 수 있다.

🌟 type or interface?
실제 배민에서 각 팀이 쓰는 방식

  • 🐱 주로 interface 사용. 컨벤션을 정한다면 공식 문서에 적힌대로 전역적으로 사용할 때는 interface를, 작은 범위 내에서 한정적으로 사용한다면 type을 써도 됨.
  • 🐙 type: 어떤 값에 대한 정의같이 정적으로 결정되어 있는 것, interface: 확장될 수 있는 basis를 정의하거나 어떤 object 구성을 설명하는 요소
  • 🐼 선언 병합 -> interface, computed value -> type

✏️ 둘 중 하나만 써야했던 상황

  • 상속하는 경우, extends나 implements를 사용할 때 interface 사용
  • 유니언 타입이나 교차 타입 등 type 정의에서만 쓸 수 있는 기능을 활용할 때 type 사용

👉 나도 실제 프로젝트에서 팀 컨벤션을 정할 때 interface를 주로 사용하되,
유니언 타입이나 교차 타입 등 type 정의에서만 쓸 수 있는 기능을 쓰자고 할 때 type을 사용하는 걸로 정했었다.

function

매개변수와 반환 값에 대한 타입을 지정

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

함수 자체의 타입 지정 -> 호출 시그니처를 정의

✏️ 호출 시그니처(Call Signature)
타입스크립트에서 함수 타입을 정의할 때 사용하는 문법.
함수 타입은 해당 함수가 받는 매개변수와 반환하는 값의 타입으로 결정된다.
호출 시그니처는 이러한 함수의 매개변수와 반환 값의 타입을 명시하는 역할을 한다.

type AddFunction = (a: number, b: number) => number;

타입스크립트에서 함수 자체의 타입을 명시할 때는 화살표 함수 방식으로만 호출 시그니처를 정의.

const add: AddFunction = (a, b) => {
  return a + b;
};

읽어주셔서 감사합니다. 잘못된 내용이나 추가로 알면 좋은 내용 있으면 댓글로 남겨주세요 🙂

profile
기존 블로그: https://hi-rachel.tistory.com

0개의 댓글