이펙티브 타입스크립트- 3장 타입 추론

fullth·2022년 7월 20일
0

Effective TypeScript

목록 보기
3/6
  • 타입스크립트는 타입 추론을 적극적으로 수행한다.

  • 타입 추론은 수동으로 명시해야 하는 타입 구문의 수를 엄청나게 줄여준다. -> 코드의 전체적인 안정성 향상.

  • 타입스크립트 초보자 vs 숙련자 -> 타입 구문의 수에서 차이 보임.

  • 전부 다 타입을 도배할 필요가 없다는 것을 알 수 있음. 불필요한 타입 구문은 필요없음.

  • 타입 추론에서 발생할 수 있는 문제와 그 해법이 3장의 내용.

  • 목표: 타입 추론이 가능한 경우라도 타입 선언을 작성해야 하는 것이 필요한 상황은 언제인지 이해하기.

아이템 19 추론 가능한 타입을 사용해 장황한 코드 방지하기

  • 결국 타입을 위한 언어 -> 변수를 선언할 때마다 타입을 명시해야 한다고 생각 -> 정곡
  • let x: number = 12; (x) -> let x = 12; 로 충분. 마우스를 올려보면 number로 이미 추론되어 있음.
    • 편집기의 타입 추론을 잘 활용하는 것이 초보자에게 중요할 것으로 보임.
  • 타입 추론이 되면 명시적 타입 구문은 필요하지 않음. 오히려 방해가 됨.
  • 타입 확신 못한다면 편집기를 통해 체크.
  • 보통 타입 정보가 있는 라이브러리에서 콜백 함수의 매개변수 타입은 자동으로 추론됨.
    • express HTTP의 서버 라이브러리를 사용하는 request, response의 타입 선언은 필요없음.
  • 추론 될 수 있는 경우여도 객체 리터럴, 함수 반환형 등은 타입 명시를 고려하는 것이 필요함.
  • 아래 소스에서 add에 마우스를 올려보면 반환형도 x: number, y: number이라고 나옴.
interface Vector2d {
  x: number;
  y: number;
}

function add (a: Vector2d, b: Vector2d) {
  return {
    x: a.x + b.x,
    y: a.y + b.y
  }
}

const test: Vector2d = {x: 1, y: 2};
const test2: Vector2d = {x: 1, y: 2};
add(test, test2);

아이템 20 다른 타입에는 다른 변수 사용하기

  • 변수의 값은 바뀔 수 있지만 그 타입은 보통 바뀌지 않는다.
    • ex.) let id = "001";
    • id = 1; (x)
    • ~~ '1' 형식은 'string' 형식에 할당할 수 없습니다.
  • 타입을 바꿀 수 있는 방법은 범위를 좁히는 것. 새로운 변수값 포함하도록 확장이 아닌, 타입을 더 작게 제한.
  • id의 타입을 바꾸지 않고 둘 다 사용하는 방법은 타입을 확장하는것.
    • let id: string | number = "001";
  • 어찌보면 당연한 이야기. 혼란을 발생하는 경우를 방지하라는 것이 포인트.

아이템 21 타입 넓히기(현재 내가 타입을 사용하는데 있어서 중요할 것 같음)

  • 런타임에 모든 변수는 유일한 값을 가짐.
  • 그러나 타입스크립트가 작성된 코드를 체크하는 정적 분석 시점에는 아님.
    • 변수는 타입을 가짐. (타입 = 가능한 값들)
  • 상수를 사용해서 변수를 초기화 할 때 타입을 명시하지 않으면, 타입 체커는 타입을 결정해야 함.
  • 즉, 타입 명시 x -> 할당된 값을 갖고, 타입 체커가 타입을 유추. 
    • 타입스크립트에서는 이러한 과정을 widening(넓히기) 라고 함.
    • 이 과정을 이해하는 것을 통해 오류의 원인을 파악하고 타입 구문을 더욱 효과적으로 사용할 수 있음.
  • 아래와 같은 코드는 런타임에서는 문제가 발생하지 않지만, 편집기에서 에러가 발생함.
  • getComponent(test, x);에서 두 번째 매개변수에 "x" | "y" | "z"를 기대했지만, x의 타입은 할당 시점에 widening이 동작해서 string으로 추론되었기 때문임.
interface wideningTest {
  x: number,
  y: number,
  z: number,
}

function getComponent(test: wideningTest, stringType: 'x' | 'y' | 'z') {
  return test[stringType];
}

let x = 'x';
let test = { x: 10, y: 20, z: 30};
getComponent(test, x);
  • widening이 진행될 때, 주어진 값으로 추론 가능한 타입은 여러 개임. 그래서 해당 과정이 모호함.
  • 아래와 같은 타입은 의도한 것과 마찬가지로 number | string의 타입으로 타입 체커가 추측함.
  • 하지만 모든 타입을 타입체커가 작성자의 의도에 맞게 추측할 수 없음.
    • 즉, 어떤 코드들이 mixed타입들이 되는지 알고 있어야 타입 체커를 의도한대로 사용할 수 있음.
const mixedType = [1, "x"];
  • 아래 코드들이 mixed 타입이 될 수 있는 타입들임.
('x' | 1)[]
['x', 1]
[string, number]
readonly [string, number]
(string|number)[]
readonly (string|number)[]
[any, any]
any[]
/**
 const test: {
    x: number;
    y: number;
  }
 */
const test = {
  x: 1, 
  y: 2  
}

/**
 const test2: {
    x: 1;
    y: number;
  }
 */
const test2 = {
  x: 1 as const, 
  y: 2  
}

/**
 const test3: {
    readonly x: 1;
    readonly y: 2;
  }
 */
const test3 = {
  x: 1,
  y: 2
} as const
  • 위 코드의 주석들은 편집기에서 마우스를 올려 타입 추론된 결과를 복사한 것.
  • as const는 타입스크립트가 최대한 좁은 타입으로 추론할 수 있게 해줌.
  • test3에는 넓히기가 동작하지 않았음.

아이템 22 타입 좁히기(?)

  • 타입 넓히기 widening의 반대.
  • 타입스크립트가 넓은 타입으로부터 좁은 타입으로 진행하는 과정을 말함.
  • 일반적인 예시는 null체크

아이템 23 한꺼번에 객체 생성하기

  • 타입스크립트의 타입은 일반적으로 변경되지 않는다(아이템 20참조)
  • 이 특성으로 일부 자바스크립트 패턴을 타입스크립트로 모델링 하는 것이 쉬워진다. (?)
    • 객체를 생성할 때 속성 하나씩 추가 X
    • 여러 속성을 포함해 한 번에 생성해야 타입 추론에 유리
const pt = {};
/**
 * '{}' 형식에 'x' 속성이 없습니다.
 */
pt.x = 3;
pt.y = 3;

interface Point { x: number, y: number; }
/**
 * '{}' 형식에 'Point' 형식의 x, y 속성이 없습니다.
 */
const pt2: Point = {};

/** 
 * Clear!! 
 * */
const pt3 = {} as Point;
pt3.x = 3;
pt3.y = 3;
  • 위와 같이 객체를 반드시 제각각 나눠서 만들어야 한다면 타입 단언문을 사용해 타입 체커를 통과할 수 있음.
  • 작은 객체를 조합해서 큰 객체를 만들어야 하는 경우도 Object.assign ~ 을 사용해서 여러 단계를 거치는 것은 좋지 않음.
const pointer = {x: 3, y: 4};
const id = {name: 'fullth'}
const namePoint = {...pointer, ...id};
namePoint.name // fullth
  • 객체 전개 연산자 ... 를 사용하면 됨.
  • 타입 걱정 없이 필드 단위로 객체를 생성할 수 있음.
  • 조건부로 속성을 추가할 수 있음.
  • 필요하지 않은 속성은 제외가능
profile
Web Backend Developer

0개의 댓글