[Learning Typescript] Ch.3 유니언과 리터럴

Typescript
Dragon C's avatar
Nov 22, 2023
[Learning Typescript] Ch.3 유니언과 리터럴
  • 타입스크립트가 값을 바탕으로 추론을 수행하는 두 가지 핵심 개념 “유니언”과 “내로잉”

  • 유니언(union): 값에 허용된 타입을 두 개 이상의 가능한 타입으로 확장하는 것

  • 내로잉(narrowing): 값에 허용된 타입이 하나 이상의 가능한 타입이 되지 않도록 좁히는 것

3.1 유니언 타입

  • “이거 혹은 저거”와 같은 타입을 유니언 이라고 함

  • 값의 정확한 타입은 모르지만 두 개 이상의 옵션 중 하나임을 알고 있는 경우 코드를 처리함

  • 타입스크립트 구문에서는 값이나 구성 요소 사이에 | 연산자를 사용하여 유니언 타입을 나타냄

// let mathematician: string | undefined
let mathematician = Math.random() > 0.5 ? undefined : "Mark Goldberg";

3.1.1 유니언 타입 선언

  • 변수의 초깃값이 있는 경우에도 변수에 대한 명시적 타입 애너테이션을 제공하는 것이 유용할 때 유니언 타입을 사용함

    // 초깃값이 null이지만 잠재적으로 string이 될 수 있음
    let thinker: string | null = null;
    if (Math.random() > 0.5) {
    	thinker = "Susanne Langer";
    }

3.1.2 유니언 속성

  • 값이 유니언 타입일 때 타입스크립트는 유니언으로 선언한 모든 가능한 타입에 존재하는 멤버 속성에만 접근할 수 있음. 그렇지 않을 경우 타입 검사 오류가 발생함

  • 예) number | string 타입인 변수가 있을 때 해당 변수에 대해 두 개의 타입에 존재하는 toString()은 사용할 수 있지만, toUpperCase()와 toFixed()는 사용할 수 없음. toUpperCase()는 number 타입에 없고, toFixed()는 string 타입에 없기 때문

  • 이는 안전 조치에 해당하는 것으로, 객체가 어떤 속성을 포함한 타입으로 확실하게 알려지지 않은 경우 해당 속성이 존재하지 않을 수도 있기에 이러한 조치를 함

  • 여러 타입 중 하나에 대한 속성을 사용하려면 코드에서 구체적인 타입을 알려주어야 하며 이를 내로잉이라고 함

3.2 내로잉

  • 값이 이전에 유추된 것보다 더 구체적인 타입임을 코드에서 유추하는 것을 내로잉이라고 함

  • 타입을 좁히는 데 사용할 수 있는 논리적 검사를 타입 가드(type guard)라고 함

3.2.1 값 할당을 통한 내로잉

  • 변수에 값을 직접 할당하면 타입스크립트는 변수의 타입을 할당된 값의 타입으로 좁힘

  • number | string 타입으로 명시한 변수에 string 값을 할당하면 값 할당 내로잉이 작동함

  • 이후에는 number, string 중 하나의 값을 받을 수 있더라도 처음에는 할당된 타입으로 시작함을 이해함

3.2.2 조건 검사를 통한 내로잉

  • 변수가 알려진 값과 같은지 확인하는 if문을 통해 변수의 값을 좁힐 수 있음

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

if (scientist === "Rosalind Franklin") {
	scientist.toUpperCase(); // Ok
}

scientist.toUpperCase(); // Error

3.2.3 typeof 검사를 통한 내로잉

  • typeof 연산자를 사용하여 변수의 값이 특정 타입에 해당하는지 확인할 수 있음

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

if (typeof researcher === "string") {
	researcher.toUpperCase();
}

typeof researcher === "string" ? researcher.toUpperCase() : researcher.toFixed()

3.3 리터럴 타입

구체적인 버전의 원시 타입인 “리터럴 타입”

  • 원시 타입 값 중 어떤 것이 아닌 특정 원싯값으로 알려진 타입

  • 변수를 const로 선언하고 직접 리터럴 값을 할당할 경우 타입스크립트는 해당 변수를 할당된 리터럴 값으로 유추함

    const philosopher = "Hypatia"; // const philosopher: "Hypatia"
  • 각 원시 타입은 해당 타입이 가질 수 있는 가능한 모든 리터럴 값의 전체 조합

    - boolean: true | false
    - null과 undefined: 둘 다 자기 자신, 즉, 오직 하나의 리터럴 값만 가짐
    - number: 0 | 1 | 2 | 3 | …… | 0.1 | 0.2 | ….
    - string: “” | “a” | “b” | “c” | …… | “aa” | “ab” | “ac” |

3.3.1 리터럴 할당 가능성

  • 0과 1처럼 동일한 원시 타입일지라도 서로 다른 리터럴 타입은 서로 할당할 수 없음

    let specificallyAda: "Ada";
    
    specificallyAda = "Ada"; // Ok
    
    specificallyAda = "Byron" // Error: Type '"Byron"' is not assignable to type '"Ada"'.

3.4 엄격한 null 검사

  • 타입스크립트는 “십억 달러의 실수(The Billion-Dollar Mistake)”를 바로잡기 위해 엄격한 null 검사(strict null checking)를 허용하며 이는 최신 프로그래밍 언어의 큰 변화 중 하나임

  • 3.4.1 십억 달러의 실수

    저는 이를 십억 달러의 실수라고 부릅니다. 1965년, null 참조의 발명으로 수많은 오류, 취약성  시스템 충돌이 발생했으며 지난 40 동안 십억 달러의 고통과 피해를 입었을 것입니다. - 토니 호어(Tonny Hoare), 2009
  • ‘십억 달러의 실수’는 다른 타입이 필요한 위치에서 null 값을 사용하도록 허용하는 많은 타입 시스템을 가리키는 업계 용어임

  • 엄격한 null 검사가 없는 언어에서는 다음과 같이 string 타입 변수에 null을 할당하는 것이 허용됨

    const firstName: string = null;
  • 타입스크립트 컴파일러는 실행 방식을 변경할 수 있는 다양한 옵션을 제공함(13장)

  • 가장 유용한 옵션 중 하나인 strictNullChecks는 엄격한 null 검사를 활성화할지 여부를 결정함

    let nameMaybe = Math.random() > 0.5 ? "Tony Hoare" : undefined;
    nameMaybe.toLowerCase();
    /* 엄격한 null 검사가 비활성화된 경우 - 오류로 판단하지 않음
    Potential runtime error: Cannot read property 'toLowerCase' of undefined.
    */
    
    /* 엄격한 null 검사가 활성화된 경우
    Error: Object is possibly 'undefined'.
    */

3.4.2 참 검사를 통한 내로잉

  • 타입스크립트는 잠재적인 값 중 truthy로 확인된 일부에 한해서만 변수의 타입을 좁힐 수 있음

  • 다음 코드에서 geneticist는 string | undefined 타입이며 undefined는 항상 falsy이므로 타입스크립트는 if문의 코드 블록에서는 geneticist가 string 타입이 되어야 한다고 추론할 수 있음

    let geneticist = Math.random() > 0.5 ? "Barbara McClintock" : undefiend;
    
    if (geneticist) {
    	geneticist.toUpperCase(); // Ok: string
    }
    
    geneticist.toUpperCase(); // Error: Object is possibly 'undefined'.
  • 논리 연산자 &&와 ?는 참 여부를 검사하는 일도 잘 수행하지만 참 확인 외에 다른 기능은 제공하지 않음

  • string | undefined 값에 대해 알고 있는 것이 falsy라면, 그것이 빈 문자열인지 undefined인지는 알 수 없음

    geneticist && geneticist.toUpperCase(); // Ok: string | undefiend
    geneticist?.toUpperCase(); // Ok: string | undefiend

3.4.3 초깃값이 없는 변수

  • 자바스크립트에서 초깃값이 없는 변수는 기본적으로 undefined가 됨

  • 값이 할당되기 전에 속성 중 하나에 접근하려는 것처럼 해당 변수를 사용하려고 시도할 경우 오류 메시지가 나타남

  • 변수 타입에 undefined가 포함되어 있는 경우에는 오류가 보고되지 않음

    let mathematician: string;
    
    mathematician?.length;
    // Error: Variable 'mathematician' is used before being assigned.
    
    let mathematician: string | undefined;
    
    mathematician?.length; // Ok

3.5 타입 별칭

  • 타입스크립트에는 재사용하는 타입에 더 쉬운 이름을 할당하는 타입 별칭(type alias)이 있음

  • type 새로운 이름 = 타입 형태를 갖음

    type RawData = boolean | number | string | null | undefined;
    
    let rawDataFirst: RawData;
    let rawDataSecond: RawData;

3.5.1 타입 별칭은 자바스크립트가 아닙니다

  • 타입 별칭은 애너테이션처럼 자바스크립트로 컴파일되지 않음

  • 위의 코드는 다음과 같이 자바스크립트로 컴파일 됨

    let rawDataFirst;
    let rawDataSecond;
  • 타입 시스템에만 존재하므로 런타임 코드에서는 참조할 수 없음

    type SomeType = string | undefined;
    
    console.log(SomeType);
    // Error: 'SomeType' only refers to a type, but is being used as a value here.

3.5.2 타입 별칭 결합

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

    type Id = number | string;
    
    // IdMaybe 타입은 다음과 같음: number | string | undefined | null
    type IdMaybe = Id  | undefined | null;

Share article

Typescript Study