아이템19 추론 가능한 타입을 사용해 장황한 코드 방지하기
타입 추론이 된다면 명시적 타입 구문은 필요하지 않다.
function square(nums: number[]) {
return nums.map(x => x * x)
}
const squares = square([1, 2, 3, 4]) // Type is number[]
타입스크립트는 예상한 것 보다 더 정확하게 추론하기도 한다
const axis1: string = 'x' // Type is string
const axis2 = 'y' // Type is "y"
타입스크립트가 추론한 y 가 더 정확한 타입이다.
아래는 기록을 위한 함수 logProduct 이다.
interface Product {
id: number
name: string
price: number
}
function logProduct(product: Product) {
const id: number = product.id
const name: string = product.name
const price: number = product.price
console.log(id, name, price)
}
여기서 각 변수에 명시된 타입들은 불필요하다.
타입스크립트에서 추론되었기 때문에 굳이 명시해줄 필요가 없다.
근데 만약 id 가 number 에서 string 으로 타입이 변경되었다고 가정해보자.
interface Product {
id: string
name: string
price: number
}
function logProduct(product: Product) {
const id: number = product.id
// ~~ Type 'string' is not assignable to type 'number'
const name: string = product.name
const price: number = product.price
console.log(id, name, price)
}
logProduct 함수 내의 명시적 타입 구문이 없는 경우에 코드는 타입 체커를 통과하지만, 명시했으므로 오류를 발생시킨다.
이때 logProduct 는 비구조화 할당문을 사용해 구현하는 것이 더 좋다.
비구조화 할당 = 구조분해할당
- 구조분해활당은 모든 지역 변수의 타입이 추론되도록 한다.
- 따라서 여기에 명시적으로 타입 구문을 넣는것은 불필요하며 코드가 번잡해진다
function logProduct(product: Product) {
const { id, name, price } = product
console.log(id, name, price)
}
객체리터럴을 정의할 때 타입이 추론될 수 있지만 타입을 명시하고 싶을 수 있다
const elmo: Product = {
name: 'Tickle Me Elmo',
id: '048188 627152',
price: 28.99,
}
객체리터럴에 타입을 정의했기 때문에 잉여속성체크가 동작한다. 만약 타입구문을 제거한다면 객체를 선언한 곳이 아니라 객체가 사용되는 곳에서 타입 오류가 발생한다.
주식시세를 조회하는 함수 getQuote 가 있다.
const cache: { [ticker: string]: number } = {}
function getQuote(ticker: string) {
if (ticker in cache) { //이미 조회한 종목은 다시 요청하지 않도록 캐시를 추가했다
return cache[ticker]
}
return fetch(`https://quotes.example.com/?q=${ticker}`)
.then(response => response.json())
.then(quote => {
cache[ticker] = quote
return quote
})
}
이때 if 문 내의 return 타입과 밖의 return 문은 타입이 다르다. if 구문에는 동일하게 Promise 가 반환되어야 한다.
따라서 if 구문에는 cache[ticker] 가 아니라 Promise.resolve(cache[ticker]) 가 반환되는 것이 옳다. 근데 ! 함수에서 오류를 발생시키고 있지 않다. 왜냐하면 타입을 명시해주지 않았기 때문에 ~
함수 반환타입을 명시해주니까 오류를 발생시키고 있다.
요약
- 타입스크립트가 타입을 추론할 수 있다면 타입 구문을 작성하지 않는게 좋다
- 이상적인 경우 함수/메서드의 시그니처에는 타입구문이 있지만, 함수 내의 지역변수에는 타입 구문이 없다
- 추론될 수 있는 경우라도 객체 리터럴과 함수 반환에는 타입 명시를 고려해야한다. 이는 내부 구현의 오류가 사용자 코드 위치에 나타나는 것을 방지해준다
아이템20 다른 타입에는 다른 변수 사용하기
function fetchProduct(id: string) {}
function fetchProductBySerialNumber(id: number) {}
let id = '12-34-56'
fetchProduct(id)
id = 123456
// ~~ '123456' is not assignable to type 'string'.
fetchProductBySerialNumber(id)
// ~~ Argument of type 'string' is not assignable to
// parameter of type 'number'
타입스크립트는 '12-34-56' 값을 보고 id 타입을 string 으로 추론했다. string 타입에는 number 를 할당할 수 없기 때문에 오류가 발생했고, 여기서 변수의 값은 바뀔 수 있지만 타입은 바뀌지 않는다는 것을 알 수 있다.
id 를 string 과 number 를 모두 포함할 수 있도록 유니온 타입으로 확장해보자
let id: string | number = '12-34-56'
fetchProduct(id)
id = 123456 // OK
fetchProductBySerialNumber(id) // OK
그렇지만 별도의 변수를 도입하는 것이 낫다. id 를 사용할 때마다 값이 어떤 타입인지 확인해야하기 때문이다.
아래와 같이 다른 타입에는 별수의 변수를 사용하는 것이 바람직하다.
- 이렇게 하면 let 대신 const 로 변수를 선언할 수 있게 되는데, const 로 변수를 선언하면 코드가 간결해지고, 타입 체커가 타입을 추론하기에도 좋다. 타입이 바뀌는 변수는 되도록이면 피하자 !
const id = '12-34-56'
fetchProduct(id)
const serial = 123456 // OK
fetchProductBySerialNumber(serial) // OK
요약
- 변수의 값은 바뀔 수 있지만 타입은 일반적으로 바뀌지 않는다.
- 혼란을 막기 위해 타입이 다른 값을 다룰 때에는 변수를 재사용하지 않도록 한다.
'Typescript' 카테고리의 다른 글
[Typescript] 이펙티브 타입스크립트 3 아이템23-25 (0) | 2023.03.15 |
---|---|
[Typescript] 이펙티브 타입스크립트 3 아이템21-22 (0) | 2023.03.13 |
[Typescript] 이펙티브 타입스크립트 2장 아이템17-18 (0) | 2023.03.07 |
[Typescript] 이펙티브 타입스크립트 2장 아이템15-16 (0) | 2023.03.07 |
[Typescript] 이펙티브 타입스크립트 2장 아이템14 (0) | 2023.03.05 |