TS스터디 이펙티브 item38~40

온호성·2024년 3월 4일
0

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

ts에서 타입 체킹의 주요 장점 중 하나는 런타임 이전에 정적으로 타입을 체크하여 잠재적 오류를 사전에 방지하는 것이다. 이러한 이점을 최대한 활용하기 위해, ts에서는 any 타입의 사용을 가능한 한 줄이는 것이 좋다. 이 장에서는 any 타입을 어떻게 효과적으로 관리하고, 안정성을 유지하면서 코드의 유연성을 높일 수 있는지에 대해 알수 있다.

함수에서 any 타입의 사용 최소화하기

함수에서 any를 사용할 때는 가능한 한 좁은 범위에서 사용해야 한다. any를 반환하게 되면, 이는 외부 코드에 any가 퍼져 타입 체커의 이점을 활용할 수 없게 만든다.

type Foo = { name: string };
type Bar = { age: number };

declare function expressionReturnFoo(): Foo;
declare function processBar(b: Bar): void;

const f1 = () => {
  const x: any = expressionReturnFoo(); // any 타입 지정
  processBar(x); // 타입 체크 없이 사용 가능

  return x; // any 반환 (위험)
};

위의 예에서 f1 함수는 any 타입을 반환하므로 외부에서 이 함수를 사용할 때 타입 체커가 제대로 동작하지 않는다. 이를 해결하기 위해 any 타입을 내부적으로만 사용하고, 외부로는 any를 반환하지 않는 방법을 사용해야 한다.

const f2 = () => {
  x = expressionReturnFoo();
  processBar(x as any); // 내부에서만 any 사용

  return x; // Foo 타입 반환
};

객체에서 any 타입의 사용 최소화하기

객체에서 any를 사용할 때도 전체에 사용하기 보다 필요한 부분에만 사용하는 것이 좋다. 전체를 any로 지정하면, 객체의 모든 속성에 대한 타입 체크를 무시하게 된다.

type Foo ={ name: string };
type Config = {
  a: number;
  b: number;
 c: { key: Foo };
};

const config: Config = {
  a: 1,
  b: 2,
  c: {
    key: 123 as any, // 부분적으로 any 사용
  },
};

any의 사용 범위는 최소으로 좁히는 것이 좋다 함수에서는 반환 타입에 절대 any를 사용하지 않아야 하며, 객체에서는 전체 대신 필요한 부분에만 any를 사용해야 한다.이렇게 any 타입 사용을 최소화 하면 더 안정적인 코드를 작성할 수 있고, 런타임에서 발생할 수 있는류를 사전에 방지할 수 있다.

-요약-

  • 타입 안전을 보장하려면, any의 활용 범위를 가능한 한 제한해야 한다.
  • 함수가 any 타입을 반환하면 타입 안전성이 저하되니, 절대로 any 타입이 반환되지 않도록 주의해야 한다.
  • 타입 오류를 강제로 제거할 때는, any보다는 @ts-ignore를 사용하는 것이 좋다.

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

ts에서 any 타입을 사용할 때는 더욱 구체적인 형태로 사용하는 것이 중요하다. any 타입은 모든 타입을 허용하기 때문에, 가능한 한 구체적인 형태로 사용하여 코드의 안정성을 확보해야 한다.

배열과 함수에서의 any 타입 활용

any 타입을 사용할 때, 가능한 한 더 구체적인 형태로 사용하는 것이 좋다. 예를 들어 배열에서 길이를 구하는 함수를 작성할 때, any 대신 any[] 를 사용하면 배열에 대한 타입 체크가 가능해진다.

function getLength(array: any[]) {
  return array.length;
}

위의 예시에서 getLength 함수는 any[] 타입을 매개변수로 받아, 함수 내의 array.length 타입을 체크하고, 반환 값의 타입을 number로 추론한다.

함수의 경우, 아래와 같이 any 대신 더 구체적인 형태를 사용할 수 있다.

type Fn0 = () => any; // 매개변수 없이 호출 가능한 모든 함수
type Fn1 = (arg: any) => any; // 매개변수 1개
type FnN = (...args: any[]) => any; // 모든 개수의 매개변수를 가진 함수

객체에서의 any 타입 활용

객체에서 any 타입을 사용할 때도, 가능한 한 더 구체적인 형태로 사용하는 것이 좋은데 예를 들어, 어떤 객체라도 받는 타입을 정할 때, object인덱스 시그니처를 사용할 수 있다.

const hasTwelveLetterKey = (obj: { [key: string]: any }) => {
  for (const key in obj) {
    if (key.length === 12) return true;
  }
  return false;
};

위의 예시에서 hasTwelveLetterKey 함수는 인덱스 시그니처를 사용하여, 모든 키를 문자열로, 모든 값을 any 타입으로 가지는 객체를 매개변수로 받는다.

any 타입을 사용할 때는 필요한지 다시 한 번 확인하고, 최대한 구체적인 형태로 사용하여 코드의 안정성을 높여야 한다. ts에서는 타입 추론과 가독성을 위해 가능한 한 any를 지양하는 것이 좋다.

-요약-

  • any 타입을 사용하기 전에, 모든 타입이 정말 필요한지 철저히 검토해야 한다.
  • any 대신에 가능한 한 구체적인 형태, 예를 들어 any[], {[key: string]: any}, 또는 () => any를 사용하여 타입을 더욱 정확하게 표현해야 한다.

🦾 item40 함수 안으로 타입 단언문 감추기

ts를 사용하다 보면, 함수 내부 로직이 복잡하게 되어 타입을 안전하게 구현하기 어려운 경우가 생길 수 있다. 이럴 때는 타입 단언문을 사용하여 타입을 강제하는 방법을 사용하게 되는데 하지만 이를 잘못 사용하면 코드의 안전성을 저해할 수 있기 때문에 주의가 필요하다.

타입 단언문을 안전하게 사용하는 기법

타입 단언문은 일반적으로 타입을 위험하게 만들지만, 상황에 따라 필요하며 현실적인 해결책이 될 수 있다. 이를 안전하게 사용하기 위한 가장 좋은 방법은 필요한 경우에만 사용하고, 이를 함수 내부에 숨기는 것다.

아래의 예시는 객체가 같은지 비교하는 함수인데 이 함수에서는 b as any라는 타입 단언문을 사용하여 b[k] 가 가능하게 만들었다. 이는 k in b 체크를 통해 b 객체에 k 속성이 있다는 것을 확인했기 때문에 안전하다고 판단할 수 있다.

function shallowObjectEqual<T extends object>(a: T, b: T): boolean {
  for (const [k, aVal] of Object.entries(a)) {
    if (!(k in b) || aVal !== (b as any)[k]) {
      return false;
    }
  }
  return Object.keys(a).length === Object.keys(b).length;
}

함수 내부에서 any 사용

함수 내부에서 any를 사용하는 경우도 있는데 아래의 예시는 마지막 호출을 캐싱하는 기능을 하는 함수이다. 이 함수에서는 any 타입을 사용하여 어떠한 함수가 인자로 들어오더라도 처리할 수 있게 만들었다.

const cacheLast = <T extends Function>(fn: T): T => {
  let lastArgs: any[] | null = null;
  let lastResult: any;
  return ((...args: any[]) => {
    if (!lastArgs || !shallowEqual(lastArgs, args)) {
      lastResult = fn(...args);
      lastArgs = args;
    }
    return lastResult;
  }) as unknown as T;
};

위의 예시에서 cacheLast 함수는 내부적으로 any 타입을 사용하지만, 외부로는 any 타입이 드러나지 않는다. 이렇게 함수 내부에서 any를 사용하되, 외부로는 안전한 타입을 드러내는 방법은 any 타입의 사용을 최소화하면서도 유연한 함수를 작성하는 데 도움이 될 수 있다.

함수 내부에서는 외부에 불필요한 정보를 노출시키지 않으면서도 타입 단언문을 활용하여 안전한 코드를 구현할 수 있다. ts를 사용하면서 타입 안정성을 유지하면서도 복잡한 내부 로직을 구현하는데 도움이 되는 타입 단언문을 적절히 활용할 수 있어야 한다.

-요약-

  • 타입 단언문은 보통 타입의 안정성을 해칠 수 있지만, 상황에 따라서는 필수적이며 실질적인 해결 방안이 될 수 있다. 이를 회피할 수 없을 때에는, 정확한 정의를 가진 함수 내부로 이를 감추는 것이 좋다.

0개의 댓글

관련 채용 정보