본문 바로가기
Book Review/Effective Typescript

Day6_Effective Typescript 스터디

by 예 강 2023. 6. 16.
2022년 12월 6일 글입니다.

CHAPTER3. 타입추론

  • ts의 핵심기능인 타입추론에 대해 배운다.
  • 타입추론은 수동으로 작성해야하는 타입의 코드를 엄청나게 줄여준다. 따라서 적절히 타입추론을 사용해야 타입스크립트를 잘 사용한다고 할 수 있으며 코드 가독성이 높아진다.

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

요약 1 : 타입스크립트가 타입을 추론할 수 있다면 타입구문은 작성 하지 않는게 좋다.
요약 2 : 이상적인 타입스크립트 = 함수/메서드의 시그니처에는 타입구문이 있지만, 함수내의 지역변수에는 타입구문이 없다.
요약 3: 추론될 수 있는 경우라도 객체리터럴과 함수 반환에는 타입 명시를 해주자. 이는 내부 구현오류가 사용자 콛에서 나타나는것을 방지해 준다.

1. 타입스크립트가 타입을 추론할 수 있다면 타입은 작성하지 않는게 좋다.

이와 같은 경우 number라는 타입작성은 불필요하다. 타입 추론이 알아서 x 의 값을 number라고 추론하기 떄문이다.


위와 마찬가지로 객체리터럴을 정의할 때 굳이 속성의 타입을 정해주지 않아도, 알아서 요소의 타입을 추론하기 때문에

이와같이 정의 해주어도 충분하다. 과한 타입선언은 거추장 스러울 뿐이다.

리팩터링 에서 타입추론의 장점

  • 모든 타입을 다 적은 케이스, 이미 매개변수에 Product가 있는데도 불구하고 타입을 하나하나 다 적어주었다.
    id 가 number 를 string으로 바꾼다고 가정했을 때, 아래 logProduct에서 오류가 남,
    (string에 number가 들어가기 때문에)

명시적 타입을 적지않고 타입추론을 사용한다면 훨씬 깔끔한 코드가 되기는 물론, 리팩터링시 오류가 나지 않는다.

2. 이상적인 타입스크립트의 경우는 함수/메서드의 시그니처에는 타입구문이 있지만, 함수내의 지역변수에는 타입구문이 없다.

  • 함수 내에서 생성된 지역변수에는 타입구문을 넣지 않는데, 이는 타입구문을 생략하여 방해되는 것들을 최소화하고 코드를 읽는 사람이 구현 로직에 집중할 수 있게 해주기 때문이다.
  • 하지만 간혹 매개변수의 타입구문을 생략하는 경우가 있다. 즉, 기본값이 있는 경우

base = 10 이라는 할당문에서 이미 base는 number라고 타입추론이 됐기 때문이다.

  • 라이브러리에 타입정보가 있는 경우

타입정보가 있는 라이브러리에서, 콜백함수의 매개변수 타입은 자동으로 추정되기 때문에 9번 라인처럼 불필요한 타입을 적을 필요 없다.

18번처럼 사용해야 한다.

3. 그렇다면 어떤경우에 타입을 명시적으로 적어주는게 좋을까?

  1. 객체 리터럴을 정의할 때 ,
const elmo:Product = {
    name : 'elmo',
    id : "112233",
    price:28,29,

}

위에 객체리터럴 시 , 과한 타입을 지정하지 말라고 했는데, 
그건 타입의 속성에 일일히 지정할필요 없다는 거였고, 이건 전체 객체의 타입을 적어주는 상황이다.

이런식으로 사용하는 이유는 잉여속성 체크가 발동되게 하기 위함인데, 잉여속성 체크가 발동되지 않으면
객체를 선언한곳이 아니라 객체가 사용되는 곳에서 타입오류가 발생되기 때문이다.

  1. 함수의 반환타입을 명시하자

반환 타입에 정확한 타입을 입력하지 않는다면 함수 구현상의 오류가 함수를 호출한 곳까지 영향력을 끼친다.
이를 방지하기 위해 반환 타입을 명시하는게 좋다.

  • 반환타입을 명시하면 함수에 대해 더욱 명확하게 알 수 있다.
  • 반환타입을 명시해서 명명된 타입을 주고받자.

적절하게 타입 명시를 하는 방법에 대해 배웠다.
1. 잉여속성 체크를 위해 객체리터럴 선언시 타입을 적어주자
2. 명확한 타입을 주고받기 위해 함수의 반환타입을 적어주자
3. 함수 바깥에서 오는 값엔 명시적으로 쓰되 함수 내부로직에 집중하기 위해 내부에선 타입스크립트 사용을 자제해보자.


Item 20. 다른 타입에는 다른 변수 사용하기

요약 1. 변수의 값은 바뀔 수 있지만 타입은 일반적으로 바뀌지 않는다.
요약 2. 혼란을 막기 위해 타입이 다른 값을 다룰 때 변수를 타입이 다르게 재사용 하지 않도록 따로 사용하자.

//javascript에선 먹히는 코드
let id = "12-34-56" 
fetchproduct(id)	//string으로 사용

id = 123456,
fetchproductBySerialNumber(id) //number로 사용
  
//typescript에선 에러가 난다.
let id = "12-34-56" 
fetchproduct(id)	//string으로 사용

id = 123456,		//string 타입을 넘버로 사용 불가.
fetchproductBySerialNumber(id)
  

이럴때, union 타입을 쓰면 되지 않나 ? 싶지만

let id : string|number = "12-34-56"
fetchproduct(id)	//정상

id = 123456,		//정상
fetchproductBySerialNumber(id)// 정상

굳이 이렇게 let을 선언하고, 변수를 재사용해서 일일히 타입체커로 변수가 string인지, number인지 체크하는 것보다

const id  = "12-34-56"
fetchproduct(id)	//정상

const serial = 123456,		//정상
fetchproductBySerialNumber(serial)// 정상

이런식으로 const로 선언한 뒤 변수를 할당하는게 더 적합하다는게 item19의 핵심이다.

무분별한 변수의 재사용 은 타입체커와 사람 모두에게 혼란을 준다.

서로 다른 타입에는 별도의 변수를 사용하는게 바람직한 이유

  • 서로 관련 없는 두 개의 값을 분리할 수 있다. (id, serial)
  • 변수명을 더 구체적으로 적을 수 있다.
  • 타입 추론을 향상시키며, 타입구문이 불필요해진다.
  • 타입이 좀 더 간결해진다.
  • let 대신 const로 변수를 선언하게 되면서 ,코드가 간결해지고 타입체커의 추론도 쉽게 도와준다.

Item 21. 타입 넓히기

요약 1. 타입넓히기 이해하기
요약2. 동작에 영향을 줄 수 있는 방법인 , const, 타입구문, 문맥, as const 기법에 익숙해지자.

타입넓히기 란 ?
상수를 사용해서 변수를 초기화 할 떄, 타입을 명시하지 않으면 타입체커는 타입을 유추해야 한다.
이말은 지정된 단일 값을 가지고, 할당 가능한 값들의 집합을 유추해야 한다는 뜻이다.
이러한 과정을 타입 넓히기라고 한다.

interface Vector3 {
  x: number;
  y: number;
  z: number;
}
function getComponent(vector: Vector3, axis: "x" | "y" | "z") {
  return vector[axis];
}

let x = "x";
let vec = { x: 10, y: 20, z: 30 };
getComponent(vec, x);

왜 x 는 'x'인데 타입체커는 string으로 유추했을까?

여기서 'x'라는 단일 타입이 할당 시점에 타입넓히기가 동작해 string으로 추론됐고, 이 때문에 'x,'y','z'에 할당이 불가능해졌다 => 오류발생!

타입넓히기가 진행될 때, 주어진 값으로 추론 가능한 타입이 여러가지이기 때문이다.

타입스크립트는 우리의 의도를 추측하려고 하지만 언제나 정확하게 추측할 수는 없다.

타입 스크립트의 넓히기의 과정을 제어해보자

  1. const
    만약 let 대신 const로 변수를 선언한다면 더 좁은 타입이 된다.

  • const로 선언됐기 때문에 x는 값을 재할당 할 수 없는 상수가 되었고
  • 타입스크립트는 타입 좁히기가 가능해져 타입을 'x'로 추론할 수 있어 오류가 사라졌다.
  1. 명시적 타입 구문을 쓰자
    객체를 사용할 땐 const 가 정답이 될 수 없다.

위처럼 얼렁뚱땅 쓰지 말고 타입을 구체적으로 적자.

  1. 타입체커에 추가적인 문맥을 제공하자 (item 26d에서 다룸)
  2. const 단언문을 사용하자 (as const)

as const를 사용하면 타입을 최대한 좁게 추론하기 때문에, 넓히기로 인해 오류가 발생된다고 생각되면, 위와같은 방법들을 사용해보자!