반응형
Jake Seo
제이크서 위키 블로그
Jake Seo
전체 방문자
오늘
어제
  • 분류 전체보기 (715)
    • 일상, 일기 (0)
    • 백준 문제풀이 (1)
    • 릿코드 문제풀이 (2)
    • 알고리즘 이론 (10)
      • 기본 이론 (2)
      • 배열과 문자열 (8)
    • 데이터베이스 (15)
      • Planet Scale (1)
      • MSSQL (9)
      • 디비 기본 개념 (1)
      • SQLite 직접 만들어보기 (4)
    • 보안 (7)
    • 설계 (1)
    • 네트워크 (17)
      • HTTP (9)
      • OSI Layers (5)
    • 회고 (31)
      • 연간 회고 (2)
      • 주간 회고 (29)
    • 인프라 (52)
      • 도커 (12)
      • AWS (9)
      • 용어 (21)
      • 웹 성능 (1)
      • 대규모 서비스를 지탱하는 기술 (9)
    • 깃 (7)
    • 빌드 도구 (7)
      • 메이븐 (6)
      • 그레이들 (0)
    • Java (135)
      • 이펙티브 자바 (73)
      • 자바 API (4)
      • 자바 잡지식 (30)
      • 자바 디자인 패턴 (21)
      • 톰캣 (Tomcat) (7)
    • 프레임워크 (64)
      • next.js (14)
      • 스프링 프레임워크 (28)
      • 토비의 스프링 (6)
      • 스프링 부트 (3)
      • JPA (Java Persistence API) (5)
      • Nest.js (8)
    • 프론트엔드 (48)
      • 다크모드 (1)
      • 노드 패키지 관리 매니저 (3)
      • CSS (19)
      • Web API (11)
      • tailwind-css (1)
      • React (5)
      • React 새 공식문서 요약 (1)
      • HTML (Markup Language) (5)
    • 자바스크립트 (108)
      • 모던 자바스크립트 (31)
      • 개념 (31)
      • 정규표현식 (5)
      • 코드 스니펫 (1)
      • 라이브러리 (6)
      • 인터뷰 (24)
      • 웹개발자를 위한 자바스크립트의 모든 것 (6)
      • 팁 (2)
    • Typescript (49)
    • 리눅스와 유닉스 (10)
    • Computer Science (1)
      • Compiler (1)
    • IDE (3)
      • VSCODE (1)
      • IntelliJ (2)
    • 세미나 & 컨퍼런스 (1)
    • 용어 (개발용어) (16)
      • 함수형 프로그래밍 용어들 (1)
    • ORM (2)
      • Prisma (2)
    • NODEJS (2)
    • cypress (1)
    • 리액트 네이티브 (React Native) (31)
    • 러스트 (Rust) (15)
    • 코틀린 (Kotlin) (4)
      • 자바에서 코틀린으로 (4)
    • 정규표현식 (3)
    • 구글 애널리틱스 (GA) (1)
    • SEO (2)
    • UML (2)
    • 맛탐험 (2)
    • 리팩토링 (1)
    • 서평 (2)
    • 소프트웨어 공학 (18)
      • 테스팅 (16)
      • 개발 프로세스 (1)
    • 교육학 (1)
    • 삶의 지혜, 통찰 (1)
    • Chat GPT (2)
    • 쉘스크립트 (1)
    • 컴파일 (2)
    • Dart (12)
    • 코드팩토리의 플러터 프로그래밍 (4)
    • 플러터 (17)
    • 안드로이드 스튜디오 (1)
    • 윈도우즈 (1)
    • 잡다한 백엔드 지식 (1)
    • 디자인 패턴 (1)

블로그 메뉴

  • 홈
  • 태그
  • 방명록

공지사항

인기 글

태그

  • 자바스크립트 인터뷰
  • 자바스크립트 면접
  • 프로그래머의 뇌
  • 참조 해제
  • item7
  • 싱글톤 패턴
  • bean Validation
  • NEXT JS
  • 서버리스 컴퓨팅
  • 작업기억공간
  • 러스트
  • 자료구조
  • 외래키 제약조건
  • 알고리즘
  • item8
  • 빈 검증
  • try-with-resources
  • serverless computing
  • 추상 팩터리 패턴
  • 자바 검증
  • 객체복사
  • 스프링 검증
  • MSSQL
  • 이펙티브 자바
  • 싱글톤
  • rust
  • 느린 쿼리
  • 이펙티브자바
  • prerendering
  • Java
  • 자바 디자인패턴
  • 메이븐 페이즈
  • 메이븐 라이프사이클
  • pnpm
  • 자바
  • next js app
  • Pre-rendering
  • 디자인패턴
  • Next.js
  • 메이븐 골
  • 자바스크립트
  • 이펙티브 자바 item9
  • Javadoc 자바독 자바주석 주석 Comment
  • 도커공식문서
  • 팩터리 메서드 패턴
  • 플라이웨이트패턴
  • item9
  • 슬로우 쿼리
  • 토비의 스프링
  • 싱글턴

최근 댓글

최근 글

티스토리

hELLO · Designed By 정상우.
Jake Seo

제이크서 위키 블로그

Typescript

TS009. 타입스크립트의 내로잉 (Narrowing) 이란?

2023. 12. 28. 09:21

Type Narrowing 이란?

  • Narrowing 이란 영어 자체의 뜻으로 봤을 때 범위를 좁힌다는 뜻이다.
  • 처음 선언한 것보다 더 구체적인 유형으로 구체화하는 프로세스를 Narrowing 이라고 한다.
  • 타입스크립트에서 어떻게 타입 유추를 정확하게 가져가는지에 대한 논리를 말한다.
  • 주요 기술로는 Type Guards (IF 로 검사), Type Assertions (수동 재정의), Control Flow Analysis (프로그램 흐름 분석) 가 있다.
  • Narrowing 을 통해 Union 타입에서 더욱 구체적인 타입으로 논리적으로 유추가 가능하다.

Narrowing 의 간단한 코드 예제

  • 아래의 예제는 Type Guards 를 이용한 예제이다.
//Narroiwng 예제
function processInput(input: string | number) {
  // Narrowing using typeof
  if (typeof input === "string") {
    console.log("Input is a string:", input);
    // 여기서, 타입스크립트는 input 이 string 이란 것을 알 수 있다.
  } else {
    console.log("Input is a number:", input);
    // 여기서, 타입스크립트는 input 이 number 란 것을 알 수 있다.
  }
}

// Example usages
processInput("Hello");
processInput(123);

Narrowing 의 종류

  • 아래의 종류는 타입스크립트 핸드북 에도 잘 나와있다.
  • 코드팩토리님 인프런 강의 에서도 잘 알려준다. 난 이걸 보면서 공부했다.
  1. Assignment Narrowing
  2. Typeof Narrowing
  3. Truthiness Narrowing
  4. Equality Narrowing
  5. In operator Narrowing
  6. Instanceof Narrowing
  7. Discriminated union Narrowing (차별된 유니언 내로잉)
  8. Exhaustiveness checking

Assignment Narrowing

  • 값을 할당하는 순간 Narrowing 이 적용되어 타입 유추가 가능해지는 것을 말한다.
// 선언할 때는 number | string 타입으로 타입이 모호함
let nOs: number | string = "abc"; // 값이 할당된 이후에 Narrowing 이 진행되어 string 타입이 됨
nOs.toUpperCase(); // string 타입에만 존재하는 toUpperCase() 메서드 이용 가능

참고: random() 함수

  • 아래 예제부터 쭉 random() 이라는 함수가 등장하는데, random() 함수가 등장하는 이유는 런타임까지 타입이 무엇인지 모르는 상태에서 어떻게 Narrowing 이 진행되는지를 보여주기 위해서이다.
  • random() 함수의 정의는 아래와 같다.
/**
 * 50% 확률로 true or false 반환
 * @returns {boolean} true or false
 */
function random() {
  return Math.random() > 0.5;
}

Typeof Narrowing

  • 자바스크립트에서 타입을 확인할 때 사용하는 typeof 를 통해 직접적으로 Narrowing 하는 방식이다.
nOs = random() ? "a" : 1;

if (typeof nOs === "string") {
  nOs.toUpperCase(); // 여기서는 당연히 string 타입 취급
} else {
  nOs.toFixed(2); // 여기서는 string 이 아니기 때문에 number 취급을 받을 수 밖에 없음
}

Truthiness Narrowing

  • if() 조건문에서 true 로 걸린다면, Narrowing 되는 방식이다.
let nullOrString: null | string[] = random() ? null : ["a", "b"];

if (nullOrString) {
  nullOrString; // string[] 취급을 받음 (null 은 if() 를 통과하지 못함)
} else {
  nullOrString; // null 취급을 받음 (if() 를 통과하지 못했으면 null 임)
}

Equality Narrowing

  • === (Equality) 키워드를 통해 Narrowing 되는 방식이다.
let numOrString: number | string = random() ? 1 : "x";
let numOrBool: number | boolean = random() ? 1 : true;

if (numOrString === numOrBool) {
  // 둘이 타입이 같으므로 여기서는 둘 다 number 취급
  numOrString;
  numOrBool;
} else {
  // 여기서는 둘이 타입이 같지는 않은 것을 확인했으므로 둘 다 number, string 혹은 number, boolean 이 될 수 있음
  numOrString;
  numOrBool;
}
let numOrStringOrUndefined: number | string | undefined = random()
  ? 1
  : random()
  ? "x"
  : undefined;

// 사실상 typeof narrowing 과도 같음
if (typeof numOrStringOrUndefined === "number") {
  numOrStringOrUndefined; // number 로 추론됨
} else {
  numOrStringOrUndefined; // number 를 제외한 string 혹은 undefined 로 추론됨
}

Inoperator Narrowing

  • 자바스크립트에서 해당 프로퍼티가 존재하는지 확인할 수 있는 in 키워드를 통해 Narrowing 하는 방식이다.
interface Person {
  name: string;
  age: number;
}

interface Company {
  location: string;
  name: string;
}

let person: Person = {
  name: "JAKE",
  age: 20,
};

let company: Company = {
  location: "SEOUL",
  name: "my crab soft",
};

let personOrCompany: Person | Company = random() ? person : company;

// 자바스크립트 `in` 키워드를 통해 `location` 이라는 프로퍼티가 존재하는지 확인
if ("location" in personOrCompany) {
  personOrCompany; // location 이라는 프로퍼티 키가 존재한다면, 무조건 Company
}

if ("name" in personOrCompany) {
  personOrCompany; // name 이라는 프로퍼티 키가 존재한다면, Person 혹은 Company 둘 다 가능
}

Instanceof Narrowing

  • 자바스크립트에서 어떤 클래스를 인스턴스화 시켰는지 확인할 수 있는 instanceof 를 통해 Narrowing 하는 방식이다.
let dateOrString: Date | string = random() ? new Date() : "abc";

if (dateOrString instanceof Date) {
  dateOrString; // Date 타입 취급
} else {
  dateOrString; // string 타입 취급
}

Discriminated Union Narrowing

  • 예제를 보면, 옵셔널(?)을 통해 모호하게 타입을 나누던 것을 옵셔널을 제거하고 더 명확하게 타입을 나눈것으로 볼 수 있다.
  • 타입스크립트 타입 설계 시에 아래와 같은 옵셔널 사용은 피하는 것이 모범 사례로 볼 수 있다.
interface SmartPhone {
  type: "galaxy" | "iphone";
  appleLogo?: true;
  samsungLogo?: true;
}

let phone: SmartPhone = random()
  ? {
      type: "galaxy",
      samsungLogo: true,
    }
  : {
      type: "iphone",
      appleLogo: true,
    };

if (phone.type === "galaxy") {
  phone.samsungLogo; // 추론 불가 (true | undefined)
} else {
  phone.appleLogo; // 추론 불가 (true | undefined)
}

interface Galaxy {
  type: "galaxy";
  samsungLogo: true;
}

interface Iphone {
  type: "iphone";
  appleLogo: true;
}

type GalaxyOrIphone = Galaxy | Iphone;

// 이전에 phone 변수에 할당했던 것과 동일하게 할당
let galaxyOrIphone: GalaxyOrIphone = random()
  ? {
      type: "galaxy",
      samsungLogo: true,
    }
  : {
      type: "iphone",
      appleLogo: true,
    };

if (galaxyOrIphone.type === "galaxy") {
  galaxyOrIphone; // Galaxy 로 정상적으로 추론됨
} else {
  galaxyOrIphone; // Iphone 으로 정상적으로 추론됨
}

Exhaustiveness Checking

  • switch ... case 문에서 유용하게 사용할 수 있다.
  • case 에 모든 type 을 검증했는지 아래의 never 타입 체크에서 확실히 검증이 된다.
switch (galaxyOrIphone.type) {
  case "galaxy":
    galaxyOrIphone; // Galaxy 타입
    break;
  case "iphone":
    galaxyOrIphone; // Iphone 타입
    break;
  default:
    galaxyOrIphone; // never 타입
    // 나중에 Galaxy, Iphone 이후에 Xiaomi 타입도 올 수 있는데
    // 이 때 확실히 모든 타입을 case 에서 처리했는지 검증 가능
    // 타입스크립트를 스마트하게 활용할 수 있는 방법 중 하나임
    const _check: never = galaxyOrIphone;
    break;
}
반응형
저작자표시 비영리 (새창열림)

'Typescript' 카테고리의 다른 글

TS011. 타입스크립트로 함수 시그니처 선언하기  (0) 2023.12.28
TS010. 타입스크립트 함수 정의 (Function Definition)  (0) 2023.12.28
TS008. Typescript Intersection 이란?  (0) 2023.12.28
TS007. 타입스크립트 Union 이란?  (0) 2023.12.28
TS006. 타입스크립트 타입 캐스팅 및 주의점  (0) 2023.12.27
    'Typescript' 카테고리의 다른 글
    • TS011. 타입스크립트로 함수 시그니처 선언하기
    • TS010. 타입스크립트 함수 정의 (Function Definition)
    • TS008. Typescript Intersection 이란?
    • TS007. 타입스크립트 Union 이란?
    Jake Seo
    Jake Seo
    ✔ 잘 보셨다면 광고 한번 클릭해주시면 큰 힘이 됩니다. ✔ 댓글로 틀린 부분을 지적해주시면 기분 나빠하지 않고 수정합니다. ✔ 많은 퇴고를 거친 글이 좋은 글이 된다고 생각합니다. ✔ 간결하고 명료하게 사람들을 이해 시키는 것을 목표로 합니다.

    티스토리툴바