[이펙티브 TS] 31 - 40 정리

개발 log·2022년 4월 13일
0

TS 지식

목록 보기
13/15
post-thumbnail

31 | 타입 주변에 null값 배치하기

  • 한 값의 null여부가 다른 값의 null여부에 암시적으로 관련되도록 설계하면 안된다.
  • API 작성 시 반환 타입을 하나의 큰 객체로 만들고 반환 타입 전체가 null이거나 null이 아니도록 설계해야 한다.
  • 클래스를 만들 때는 필요한 모든 값이 준비되었을 때 생성하도록 하여 null이 존재하지 않도록 해야 한다.
  • strictNullChecks를 설정하면 코드에 많은 오류가 표시되겠지만, null값과 관련된 문제점을 찾아낼 수 있기 때문에 반드시 설정해야 한다.


32 | 유니온의 인터페이스보다는 인터페이스의 유니온을 사용하기

  • 만약 인터페이스 내에서 유니온을 사용하고 있다면 인터페이스끼리 유니온으로 묶는 것이 더 용이하지 않을지 고려해야 한다.
  • 유니온의 인터페이스보다 인터페이스의 유니온이 더 정확하고 TS가 이해하기도 좋다.
  • TS가 제어 흐름을 분석할 수 있도록 타입에 태그를 넣는 것을 고려해야 한다.
interface Layer {
  layout: FillLayout | LineLayout | PointLayout;
  paint: FillPaint | LinePaint | PointPaint;
}

// 아래 형태로 작성하면 잘못된 조합으로 섞이는 경우를 방지할 수 있다.
// 또한 태그드 유니온도 사용하기 용이해진다.
interface FillLayer {
  type: 'fill';
  layout: FillLayout;
  paint: FillPaint;
}

interface LineLayer {
  type: 'line';
  layout: LineLayout;
  paint: LinePaint;
}

interface PointLayer {
  type: 'paint';
  layout: PointLayout;
  paint: PointPaint;
}

type Layer = FillLayer | LineLayer | PointLayer;


33 | string타입보다 더 구체적인 타입 사용하기

  • string을 남발하여 선언된 코드는 any와 다를바가 없다.
  • 때문에 가능하다면 string보다 더 구체적인 타입을 사용하는 것이 좋다.
    • 문자열 리터럴 타입 유니온
    • 객체 속성이름을 받을 때는 keyof T


34 | 부정확한 타입보다는 미완성 타입을 사용하기

  • 타입 안정성을 높이기 위해 완벽을 추구하다가 부정확한 타입을 사용하게 되는 경우가 발생하지 않도록 해야한다.
  • 타입이 없는 것보다 잘못된 타입이 더 안 좋다.
  • 타입을 정확하게 모델링할 수 없다면 차라리 범위를 넓게 지정하는 형식으로 모델링하는 것이 낫다.
  • anyunknown을 구별해서 사용해야 한다.


35 | 데이터가 아닌, API와 명세를 보고 타입 만들기

  • 데이터에 드러나지 않는 예외적인 경우들이 문제가 될 수 있으므로 데이터보다는 명세를 기반으로 모델링하는 것이 낫다.
  • 코드의 타입 안전성을 위해 API또는 데이터 형식에 대한 타입 생성을 고려해야 한다.


36 | 해당 분야의 용어로 타입 이름 짓기

가독성을 높이고, 추상화 수준을 올리기 위해 타입을 정의할 때 해당 분야의 용어로 정의해야한다.

타입, 속성, 변수에 이름을 붙일 때 명심해야 할 세 가지 규칙

  • 동일한 의미를 나타낼 때는 같은 용어 사용
  • data, info, thing, item, object, entity같은 범위가 모호한 이름은 피해야한다.
  • 이름을 지을 때는 포함된 내용이나 계산 방식이 아니라 데이터 자체가 무엇인지를 나타내야 한다.


37 | 공식 명칭에는 상표를 붙이기

TS는 구조적 타이핑을 사용하기 때문에 값을 세밀하게 구분하지 못하는 경우가 있다.

값을 구분하기 위해 공식명칭이 필요하다면 상표(brand)를 붙이는 것도 고려해야 한다.

type Meters = number & { _brand: 'meters' };
type Seconds = number & { _brand: 'seconds' };

const meters = (m: number) => m as Meters;
const seconds = (s: number) => s as Seconds;

const oneKm = meters(1000); // type is Meters
const oneMin = seconds(60); // type is Seconds

// 하지만 산술 후에는 타입이 `number`로 초기화됨
const tenKm = oneKm * 10; // type is number
const oneHour = oneMin * 60; // type is number

상표 사용법

type AbsolutePath = string & { _brand: 'abs' };

function isAbsolutePath(path: string): path is AbsolutePath {
  return path.startsWith('/');
}

function listAbsolutePath(path: AbsolutePath) {}

function useAbsolutePath(path: string) {
  if (isAbsolutePath(path)) {
    listAbsolutePath(path);
  }
  // `string`형식의 인수는 `AbsolutePath`형식의 매개변수에 할당될 수 없다.
  listAbsolutePath(path);
}


38 | any타입은 가능한 한 좁은 범위에서만 사용하기

  • 의도치 않은 타입 안전성의 손실을 피하기 위해 any의 사용 범위를 최소한으로 좁혀야 한다.
  • 함수의 반환 타입을 any로 지정하면 타입 안정성이 나빠진다.
    • any타입을 반환하는 건 지양수준이 아니라 엄격하게 금해야 한다.
  • 강제로 타입 오류를 제거하려면 any대신 @ts-ignore를 사용하는 것이 좋다.

이렇게 사용하는 것 자체에 오류가 있는 코드이긴 하나 예제로서 사용됨

  • Foo 타입을 Bar를 매개변수로 받는 함수에 전달한다는 것 자체가 말이 안됨
function f1() {
  const x = expressionReturningFoo();

  // @ts-ignore
  processBar(x);
  return x;
}


39 | any를 구체적으로 변형해서 사용하기

  • any를 사용할 때는 정말 모든 값이 허용되어야만 하는지 아주아주 면밀히 검토해야 함
    • 그냥 사용하지 말기
  • 정말 만약에 사용해야 한다면 타입 시스템을 활용할 수 있도록 조금 더 구체적으로 모델링해야 함
    • any[], { [id: string]: any }, () => any...


40 | 함수 안으로 타입 단언문 감추기

  • 타입 선언문?(단언문이 맞는듯)은 타입을 위험하게 만들지만 상황에 따라 필요하기도 하고 현실적인 해결책이 되기도 한다.
  • 불가피하게 사용해야 한다면 타입 범위를 확실하게 지정한, 즉 응집도 있는 함수 몸체에 작성하는 것이 좋다.
profile
프론트엔드 개발자

0개의 댓글

관련 채용 정보