[CH03] Item 23 : 한꺼번에 객체 생성하기

유지민·2023년 8월 23일
0

Effective-Typescript

목록 보기
1/5
post-thumbnail

Item 23 : 한꺼번에 객체 생성하기

Item 20: “변수의 값은 바뀔 수 있지만, 그 타입은 보통 바뀌지 않는다.”
→ JS 패턴을 TS로 모델링하는 것이 쉬워짐
→ 여러 속성을 포함해 한꺼번에 생성하는 것이 타입 추론에 유리
(worse. 객체 생성 시 속성을 하나씩 추가하는 방식)

/** JS에서 2차원 점을 표현하는 객체 생성법 */
// TS에서 발생하는 할당문에서의 오류
const pt = {};
pt.x = 3; // ~ '{}' 형식에 'x'속성이 없습니다.
pt.y = 4; // ~ '{}' 형식에 'y'속성이 없습니다.
  • 첫 줄의 pt 타입 : {}값을 기준으로 추론되므로
    → 존재하지 않는 속성의 추가 불가능
  • Point 인터페이스 추가 시 오류 변경 내용
interface Point { x: number; y: number;}
const pt: Point = {}; // ~ '{}' 형식에 'Point' 형식의 x, y 속성이 없습니다.
pt.x = 3;
pt.y = 4;

sol. 객체를 한 번에 정의

const pt = {
	x: 3,
	y: 4,
}; // 정상
  • 객체를 반드시 제각각 나눠 만들어야 하는 경우, 타입 단언문(as) 사용해 타입 체커 통과
const pt = {} as Point;
pt.x = 3;
pt.y = 4; // 정상
// 위의 경우도 선언 시 객체를 한꺼번에 만드는 것이 나음
const pt: Point = {
	x: 3,
	y: 4,
};
  • 작은 객체를 조합해 큰 객체를 만드는 경우

    • Worst case : 여러 단계를 거치는 경우
      const pt = {x: 3, y: 4};
      const id = {name: 'Pythagoras'};
      const namedPoint = {};
      Object.assign(namedPoint, pt, id);
      namedPoint.name;
      	// ~~~ '{}' 형식에 'name' 속성이 없습니다.
    • Best case : 객체 전개 연산자()를 사용하는 경우
      const namedPoint = {...pt, ...id};
      namedPoint.name; // 정상, 타입이 string
  • 객체 전개 연산자 사용 시 이점

    • 큰 객체 한꺼번에 생성 가능
    • 타입 걱정 없이 필드 단위로 객체 생성 가능
      → 업데이트마다 새 변수를 사용해 각각 새로운 타입을 얻도록 해야 함
      const pt0 = {};
      const pt1 = {...pt0, x: 3};
      const pt: Point = {...pt1, y: 4}; // 정상
      : 간단한 객체 생성을 위해 우회하였으나, 객체 속성 추가 & TS의 새로운 타입 추론에 유용
  • 타입에 안전한 방식으로 조건부 속성 추가 시 : null or {}로 객체 전개 사용

    declare let hasMiddle: boolean;
    const firstLast = {first: 'Harry', last: 'Truman'};
    const president = {...firstLast, ...(hasMiddle ? {middle: 'S'} : {})};
    const president: {
    	middle?: string;
    	first: string;
    	last: string;
    }
  • 전개 연산자 사용 → 한꺼번에 여러 속성 추가

    ```jsx
    declare let hasDates: boolean;
    const nameTitle = {name: 'Khufu', title: 'Pharaoh'};
    const pharaoh = {
    	...nameTitle,
    	...(hasDates ? {start: -2589, end: -2566} : {})
    };
    ```  
    ```jsx
    /** 유니온 타입으로 추론되는 pharaoh */
    const pharaoh: {
    	start: number;
    	end: number;
    	name: string;
    	title: string;
    } | {
    	name: string;
    	title: string;
    }
    ```  
    - → 해당 타입에서는 start를 읽을 수 없음    
        ```jsx
        pharaoh.start
        		// ~~~~~~ '{ name: string; title: string; }' 형식에
        		//        'start' 속성이 없습니다. 
        ```  
        해당 경우는 `start`와 `end`가 항상 함께 정의됨  
        → 유니온을 사용하는 것이 가능한 값의 집합을 더 정확히 표현 O  
        유니온보다 선택적 필드가 다루기에 더 쉬울 수 있음  
    
        - 선택적 필드 방식으로의 표현법 → 헬퍼 함수 사용  
        ```jsx
        function addOptional<T extends object, U extends object>(
        	a: T, b: U | null
        ): T & Partial<U> {
        	return {...a, ...b};
        }
        
        const pharaoh = addOptional(
        	namedTitle,
        	hasDates ? {start: -2589, end: -2566} : null
        );
        
        pharaoh.start // 정상, 타입이 number | undefined
        ```  

    객체나 배열을 변환해 새로운 객체나 배열을 생성하고 싶은 경우
    → 루프 대신 내장된 함수형 기법 or 로대시(Lodash)와 같은 util 사용 권장
    → 한꺼번에 객체 생성하기 관점에서 더욱 옳은 방식

요약

  • 속성 제각각 추가 X → 한꺼번에 객체로 만들 것
  • 안전한 타입으로 속성 추가 희망 시 : 객체 전개({…a, …b}) 사용
  • 객체에 조건부로 속성을 추가하는 방식 익히기
profile
끊임없이 도전하며 사고하는 주니어 Web 개발자 유지민입니다.

0개의 댓글