[타입시스템 12] 함수 표현식에 타입 적용하기

2022. 7. 18. 16:03책/이펙티브 타입스크립트

0. statement vs expression

  • statement : 여러 토큰의 집합이다
  • expression : 값으로 평가될 수 있는 statement이다
  • 당연한 소리지만 JS/TS 모두 함수에 대한 statement(문)와 expression(표현식)을 다르게 인식한다.

    * 토큰 : 문법적인 의미를 가지는, 문법적으로 더 이상 나눌 수 없는 코드의 기본 요소

 

1. TS에선 함수 표현식 사용이 더 좋다

  • 함수의 매개변수부터 반환값까지 전체를 함수 타입으로 선언해 함수 표현식에 재사용할 수 있기 때문
  • 코드 자체도 간결하고 안전해진다
  • 특히, 함수 시그니처가 반복되는 유사한 형태라면 하나의 함수 타입으로 통합해 간결하게 쓸 수 있다

예시1)  반복되는 함수 시그니처를 하나의 함수 타입으로 통합, 재사용

하나의 타입만 선언해두면 불필요한 코드 반복을 줄일 수 있다

type BinaryFn = (a :number, b :number) => number;
const add :BinaryFn = (a, b) => a + b; // 표현식
const sub :BinaryFn = (a, b) => a - b; // 표현식
const mul :BinaryFn = (a, b) => a * b; // 표현식

// 문(statement)
// BinaryFn type을 할당할 수 없다
function add2(a, b) { return a + b }


예시2) 동일한 함수 시그니처를 사용한다면 typeof

fetch는 Promise를 반환한다. 문제는 fullfilled, rejected에 따라 서버에서 전송받는 데이터 타입이 다를 수 있다는 거다. 게다가 fetch는 요청 실패시 거절된 프로미스를 응답하지도 않는다.
 
ex) fulfilled : { data: '옛다 필요한 데이터' } // 보통 JSON 형태로 온다
ex) rejected : '이상한 요청일세?' // JSON이 아닌 무언가가 올 수도 있다

 

이럴 때 fetch의 서버 요청 상태를 체크해줄 함수를 fetch와 동일한 시그니처로 만들 수 있다.

declare function fetch(
  input: RequestInfo | URL, 
  init?: RequestInit | undefined
): Promise<Response> 

// 함수 statement
// input, init, 반환결과의 타입은 어차피 fetch의 타입과 동일하다
// 즉, 동일한 시그니처를 사용하는 또 다른 함수일 뿐이다
async function checkFetch(
  input :RequestInfo | URL, 
  init? :RequestInit | undefined
) :Promise<Response> {
  const response = await fetch(input, init)
  if (!response.ok) {
    throw new Error(`Response Failed: ${response.status}`);
  }
  return response;
}

// typeof fetch로 함수 타입을 추출하고
// 그대로 함수 표현식에 할당하면 구구절절 타입 안달아줘도 된다
const checkFetch2 :typeof fetch = async (input, init) => {
  const response = await fetch(input, init)
  if (!response.ok) {
    throw new Error(`Response Failed: ${response.status}`);
  }
  return response;
}