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

2023. 2. 1.·🎨 프론트엔드 공부/JS & TS

이펙티브 타입스크립트 (댄 밴더캄 지음) 를 읽고 정리

📍요약

✅타입 추론이 가능하다면, 타입 구문을 명시하지 않는게 좋다

✅가능한 함수 시그니처에는 타입을 명시하고, 실제 함수의 지역 변수에는 타입 구문을 명시하지 않는다

✅타입 추론이 가능한 경우에도 객체 리터럴과 함수 반환값에는 타입 명시를 고려할 필요가 있다

(내부 구현의 오류가 사용자 코드 위치에 나타나는 것을 방지)

 

📍타입 추론의 장점

수동으로 명시해야 하는 타입 구문의 수를 줄여, 코드의 전체적인 안정성 향상

- 따라서 필요한 곳에만 타이핑해야 한다

 

📍타입 추론을 사용해야 하는 이유

복잡한 객체도 타입 추론 가능

- 타입을 생략해도 된다

const person = {
  name: 'Sojourner Truth',
  born: {
    where: 'Swartekill, NY',
    when: 'c.1797',
  },
  died: {
    where: 'Battle Creek, MI',
    when: 'Nov. 26, 1883'
  }
};

 

타입스크립트가 더 정확할 수도 있다

const axis1: string = 'x';  // Type is string
const axis2 = 'y';  // Type is "y"

let axis2 = 'y';  // Type is string

 

- axis2의 타입은 "y"

(string이 아님)

⭐그런데 const 대신 let을 쓰면 문자열로 타입 추론한다

 

📍구조분해할당을 써야 하는 이유

- 구조분해할당은 모든 지역 변수의 타입이 추론되도록 함

 

📍기본값이 있는 매개변수도 타입이 추론된다

📍타입 정보가 있는 라이브러리에서 콜백 함수의 매개변수 타입도 자동으로 추론됨

- express 라이브러리 예시

// HIDE
namespace express {
  export interface Request {}
  export interface Response {
    send(text: string): void;
  }
}
interface App {
  get(path: string, cb: (request: express.Request, response: express.Response) => void): void;
}
const app: App = null!;
// END

// Don't do this:
app.get('/health', (request: express.Request, response: express.Response) => {
  response.send('OK');
});

// Do this:
app.get('/health', (request, response) => {
  response.send('OK');
});

 

- request, response 객체의 타입을 할당할 필요 없음

 

📍타입 추론될 수 있지만, 타입 명시가 필요한 경우1

- 객체 리터럴의 타입을 명시할 때

 

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);
}

// 타입 명시 -> 실제로 에러 발생하는 부분을 감지. Good
 const furby: Product = {
   name: 'Furby',
   id: 630509430963,
// ~~ Type 'number' is not assignable to type 'string'
   price: 35,
 };
 logProduct(furby);
 
 // 타입 추론 -> 객체가 쓰이는 곳 (매개변수) 에서 에러 발생. Bad
 const furby = {
  name: 'Furby',
  id: 630509430963,
  price: 35,
};
logProduct(furby);
        // ~~~~~ Argument .. is not assignable to parameter of type 'Product'
        //         Types of property 'id' are incompatible
        //         Type 'number' is not assignable to type 'string'

 

📍타입 추론될 수 있지만, 타입 명시가 필요한 경우2

- HTTP 요청을 보내고 응답값을 타이핑할 때

- 예시) 주식 가격 API 에 요청보내는 함수

// 받아온 응답이 있으면 캐싱
const cache: {[ticker: string]: number} = {};

// 주식 가격 조회 비동기 함수
function getQuote(ticker: string) {
  // 캐싱된 결과를 리턴
  if (ticker in cache) {
    return cache[ticker]; // 반환1
  }
  // 없으면 HTTP 요청
  return fetch(`https://quotes.example.com/?q=${ticker}`)
      .then(response => response.json())
      .then(quote => {
        cache[ticker] = quote;
        return quote; // 반환2
      });
}

 

그런데 이 코드에 오류가 있다

- fetch 함수의 반환값은 무조건 Promise이므로, 반환2의 결과는 Promise이다

- 따라서 캐시된 반환1도 Promise이어야 한

 

그런데 반환 타입을 정하지 않으면, 실제 에러의 원인이 아닌 곳에서 에러가 발생한다

function considerBuying(x: any) {}
// 엄한 곳에서 에러 발생
getQuote('MSFT').then(considerBuying);
              // ~~~~ Property 'then' does not exist on type
              //        'number | Promise<any>'
              //      Property 'then' does not exist on type 'number'

 

⭐반환 타입을 명시해주면 정확한 위치에 에러가 표시된다

- 반환 타입을 Promise<number> 로 명시

const cache: {[ticker: string]: number} = {};
function getQuote(ticker: string): Promise<number> {
  if (ticker in cache) {
    return cache[ticker];
        // ~~~~~~~~~~~~~ Type 'number' is not assignable to 'Promise<number>'
  }
  // COMPRESS
  return Promise.resolve(0);
  // END
}

 

📍타입 추론될 수 있지만, 타입 명시가 필요한 경우3

- 명명된 타입을 사용하기 위해

- 예시)

interface Vector2D { x: number; y: number; }

function add(a: Vector2D, b: Vector2D) {
  return { x: a.x + b.x, y: a.y + b.y };
}
/* 함수 시그니처
add(a: Vector2D, b: Vector2D): {
    x: number; 
    y: number;
} -> 단순히 number 객체로 추론됨

명명된 타입이 반환 타입으로 지정되면 좋겠어!
*/

// 반환 타입에 명명된 타입으로 표시됨
function add(a: Vector2D, b: Vector2D): Vector2D {
  return { x: a.x + b.x, y: a.y + b.y };
}

 

 

 

'🎨 프론트엔드 공부/JS & TS' 카테고리의 다른 글
  • #21 타입 넓히기
  • #20 다른 타입에는 다른 변수 사용하기
  • #18 매핑된 타입을 사용하여 값을 동기화하기
  • #17 변경 관련된 오류 방지를 위해 readonly 사용하기
지식물원
지식물원
지식이 자라는 식물원!
  • 지식물원
    지식물원
    지식물원
  • 전체
    오늘
    어제
    • 분류 전체보기 (516) N
      • 🎨 프론트엔드 공부 (253) N
        • JS & TS (92) N
        • HTML & CSS (22)
        • React & Next (49)
        • Vue & Nuxt (22)
        • 기타 (68)
      • 🤓 기술 학습 & 공부 기록 (116)
        • Node.js (0)
        • Python (37)
        • 백엔드 (0)
        • 딥러닝 (1)
        • 컴퓨터 일반 (72)
        • 개발 인프라 (6)
      • 👨‍💻 프로젝트 경험 (6)
        • Work (0)
        • Toy (6)
      • ⚙️ 개발 팁 & 노하우 (21)
        • 프론트엔드 (6)
        • 기타 (15)
      • ☕️ 커리어 & 인터뷰 준비 (88)
        • 코딩 테스트 (88)
      • 📰 기술 트렌드 & 생각 정리 (4)
      • 📚 기타 (25)
        • 마케팅 (15)
        • 비개발서적 (10)
  • 블로그 메뉴

    • 태그
  • 링크

  • 공지사항

    • 모바일 접속 시 코드 하이라이팅 깨질 때
  • 인기 글

  • hELLO· Designed By정상우.v4.10.3
지식물원
#19 추론 가능한 타입을 사용해 장황한 코드 방지하기
상단으로

티스토리툴바