러닝 타입스크립트 | ch 3. 유니언과 리터럴

doodoo·2023년 2월 26일
0
post-thumbnail

타입스크립트가 해당 값을 바탕으로 추론을 수행하는 두 가지 핵심 개념

  • 유니언: 타입 확장하기
  • 내로잉: 타입 좁히기


유니언(Union) 타입

타입 확장 (OR)

  • 유니언 타입은 정확히 어떤 타입인지 모르지만 두 개 이상의 옵션 중 하나인 경우 사용한다.
  • 수직선 | 연산자를 사용해서 유니언 타입을 나타낸다.
type1 | type2

유니언 타입 선언

유니언 타입 선언은 타입 애너테이션으로 타입을 정의하는 모든 곳에서 사용 할 수 있다.

// 초기값은 null 이지만, 잠재적으로 null 대신 string이 될 수 있음을 알려줌
let thinker: string | null = null;

유니언 속성

유니언으로 선언한 모든 타입에 존재하는 멤버 속성에만 접근 할 수 있다.

// string | number
let physicist = Math.random() > 0.5 ? "Marie Curie" : 84;

physicist.toString(); // OK

physicist.toUpperCase();
// Error: Property 'toUpperCase' does not exist on type 'string | number'.
// Property 'toUpperCase' does not exist on type 'number'.
  • number 타입과 string 타입에 모두 존재하는 toString()은 사용할 수 있다.
  • 하지만 toUpperCase()는 number 타입에 없기 때문에 사용할 수 없다.
  • 유니언 타입으로 정의된 여러 타입 중 하나의 타입으로 된 값의 속성을 사용하려면 타입을 구체적으로 알려야 한다. 이 과정을 내로잉이라 한다.


내로잉(Narrowing)

타입 좁히기

  • 내로잉은 값이 정의, 선언, 이전에 유추된 것보다 더 구체적인 타입임을 코드에서 유추 하는것
  • 타입을 좁히는 데 사용할 수 있는 논리적 검사를 타입 가드라고 한다.
    • 타입을 좁힐 때 흔히 사용하는 타입 가드 두 가지
      1. 값 할당
      2. 조건 검사

값 할당을 통한 내로잉

변수에 값을 직접 할당하면 할당된 값의 타입으로 좁힐 수 있다.

✔️ 예시 (1)

let color: string | number;

color = "red"; // string 값을 직접 할당 

color.toString(); // string으로 타입이 좁혀짐 

color.toFixed(); // Error: Property 'toFixed' does not exist on type 'string'.
  • color 변수에 string 또는 number로 선언했다.
  • 이후 string 값이 할당되어서 타입스크립트는 color 변수가 string 타입이라는 것을 알게된다.
  • 그래서 toString()은 사용할 수 있지만 number 타입에만 있는 toFixed()는 사용할 수 없다.

✔️ 예시 (2)

let color: string | number = "red"; // 선언과 동시에 값 할당 

color.toString(); // string으로 타입이 좁혀짐 

color.toFixed(); // Error: Property 'toFixed' does not exist on type 'string'.
  • 변수에 유니언 타입 애너테이션이 명시되고 바로 초기값을 string으로 할당했다.
  • 타입스크립트는 즉시 string 타입으로 좁혀졌다는 것을 알고 있다.
  • 그래서 toString()은 사용할 수 있지만 toFixed()는 사용할 수 없다.

조건 검사를 통한 내로잉

  • 일반적으로 타입스크립트에서는 if문을 통해 변수의 값을 좁히는 방법을 사용한다.
  • if문 내에서 변수가 알려진 값과 동일한 타입인지 확인한다.
let scientist = Math.random() > 0.5 ? "Rosalind Franklin" : 51;

if(scientist === "Rosalind Franklin") {
    scientist.toUpperCase(); // scientist: string 타입 
}

// scientist: string | number 타입
scientist.toUpperCase();
// Error: Property 'toUpperCase' does not exist on type 'string | number'.
// Property 'toUpperCase' does not exist on type 'number'.

typeof 검사를 통한 내로잉

직접 값을 확인해서 타입을 좁히기도 하지만, typeof 연산자를 사용할 수도 있다.

let scientist = Math.random() > 0.5 ? "Rosalind Franklin" : 51;

if(typeof scientist === "string") {
    scientist.toUpperCase(); // scientist: string 타입 
} else {
    scientist.toFixed(); // scientist: number 타입 
}
// 삼항연산자 사용 
typeof scientist === "string" ? scientist.toUpperCase() : scientist.toFixed();

참 검사를 통한 내로잉

truthy한 값은 boolean 문맥에서 true로 간주된다. 이를 통해 변수의 타입을 좁힐 수 있다.

let geneticist = Math.random() > 0.5 ? "Barbara McClintock" : undefined; // string | boolean

if(geneticist) {
    geneticist.toUpperCase(); // OK: string
}

geneticist.toUpperCase(); // Error: 'geneticist' is possibly 'undefined'.
  • geneticist의 타입은 string 또는 undefined이다. undefined는 항상 falsy한 값.
  • 타입스크립트는 if문 안에서 geneticist가 string 타입이 되어야 한다고 추론할 수 있다.
geneticist && geneticist.toUpperCase(); // OK: string | undefined
geneticist?.toUpperCase(); // OK: string | undefined
  • 논리연산자 &&?를 사용해 참 여부를 검사할 수 있다.
  • 하지만 참 여부 확인만 가능하다. falsy한 값이 빈 문자열인지 undefined인지는 알 수 없다.
let geneticist = Math.random() > 0.5 && "Barbara McClintock";


if(geneticist) {
    geneticist; // 타입: string
} else {
    geneticist; // 타입: string | false
}
  • geneticist의 타입은 string 또는 undefined
  • if문에서 string으로 타입을 좁힐 수 있지만, else 문에서 geneticist가 빈 문자열인 경우 여전히 string이 될 수 있다.


리터럴 타입

더 구체적인 원시 타입

let color: "red";
color = "red"; // OK
color = "blue"; // Error: Type '"blue"' is not assignable to type '"red"'.


엄격한 null 검사

strictNullChecks 옵션을 true로 설정하면 코드가 null 또는 undefined 값으로 인한 오류로부터 안전한지 여부를 파악할 수있다.



타입 별칭

유니언 타입은 대부분 2~3개의 타입을 옵션으로 갖는데, 만약 타입이 이것보다 훨씬 많아진다면 입력하기 불편할것이다. 이런 상황에서 유용하게 사용할 수 있는게 타입 별칭(type alias)이다.
타입 별칭을 사용하면 원하는 타입을 만들 수 있고, 재사용하기 편하다는 장점이 있다.


문법

type NewName = type
  • type 키워드를 사용하고 새로운 이름을 지정한다.
  • 그리고 = 다음에 지정하고 싶은 타입을 적는다.
  • 이름은 파스칼 케이스로 작성한다.

예시

let rawDataFirst: boolean | number | string | null | undefined;
let rawDataSecond: boolean | number | string | null | undefined;
let rawDataThird: boolean | number | string | null | undefined;

똑같은 유니언 타입이 계속 반복되고 있다. 이렇게 매번 똑같이 입력해야 하는 타입은 타입 별칭으로 간단하게 만들 수 있다.

type RawData = boolean | number | string | null | undefined;

let rawDataFirst: RawData;
let rawDataSecond: RawData;
let rawDataThird: RawData;


타입 별칭 결합

타입 별칭은 다른 타입 별칭을 참조할 수 있다.

type Id = number | string;

// IdMaybe의 타입: number | string | undefined | null
type IdMaybe = Id | undefined | null;

타입 별칭 선언의 순서는 상관없다. 타입 별칭을 먼저 선언하고 참조할 타입 별칭을 나중에 선언해도 된다.

type IdMaybe = Id | undefined | null;
type Id = number | string;

0개의 댓글