String과 다른 String Literal

조민호·2023년 5월 23일
0

TypeScript는 JavaScript의 슈퍼셋이다

그렇지만 JavaScript의 관점으로 접근하다가 매우 이상하게 느껴지는 개념이 몇 개가 존재한다




String 말고 String Literal


Type Aliases에 타입 대신에 값을 넣어 줄 수 도 있다는 것을 배우면서
처음에는 단순히 “타입대신 값을 넣어줄 수 있구나” 라고만 이해하고 넘어갔었지만 다시 한번 짚고 넘어가자면

const a = "Hello World"
let b = "Hello World"
const c: string = "Hello World"

위의 코드를 보면 JS에 익숙한 경우, a b c 모두 다 string타입이라고 생각하기 쉽다. 그렇지만 TS에서는 아니다

  • a는 "Hello World" 타입이고

  • b 와 c 는 string 타입이다

b 변수는 let으로 선언되어 재할당될 수 있을 경우 어떤 문자열이든 넣을 수 있으며 그 경우의 수가 무한대이다.
그렇기 때문에 컴파일러는 이 변수를 string 타입으로 추론한다.

c 변수는 명시적으로 string 타입으로 선언했으므로 그냥 string 타입이다.

하지만, a의 경우는 컴파일러가 이 변수를 string이 아닌 조금 더 좁은 타입(narrowed type)으로 선언한 것으로 추론한다. (=Literal Narrowing)


그러므로 a의 타입은 string이 아니라 string타입을 좁혀 만든 string literal type이다.

여기서 "타입을 좁힌다"는 말의 의미는 무한대의 경우의 수를 가질 수 있는 string타입보다 훨씬 구체적인 string의 부분집합인  "Hello World"만을 허용하는 타입을 선언했다는 뜻이다

따라서 아래와 같이 명시적으로 literal type을 선언하면 let 으로 선언된 변수도 "Hellow World" 타입만을 허용하도록 만들 수도 있다.

type HelloWorldType = "Hello World" // literal type

let a: HelloWorldType = "Hello World" // ok
a = "hahaha" // compile error



String Literal의 유용함


  1. string literal 타입은 열거형 타입처럼 사용할 때 매우 유용하다

예를 들어 마우스 이벤트를 처리하는 함수가 있다고 하자.

마우스 이벤트의 종류는 이미 정해져 있다. (mouseout , mouseover , click 중 하나)

JavaScript의 방법대로 이벤트 이름을 string 타입으로 받을 수 있지만

오타 혹은 유효하지 않은 이벤트 이름으로 인해 발생하는 런타임에러를 사전에 방지할 수 없다

function handleEvent(event: string) {}
handleEvent("click")
handleEvent("clock") // compile error: 오타. 컴파일 타임에 발견할 수 없다.
handleEvent("hover") // compile error: 유효하지 않은 이벤트 이름. 마찬가지로 컴파일 타임에 걸러낼 수 없다.

이떄 string literal 타입 조합만을 허용하도록 하도록 수정한다

type EventType = "mouseout" | "mouseover" | "click"
function handleEvent(event: EventType) {}
handleEvent("click")
handleEvent("hover") // compile error: Argument of type '"hover"' is not assignable to parameter of type 'EventType'.
  1. String Literal키를 이용한 객체 접근도 가능하다

TypeScript는 기본적으로 객체의 프로퍼티를 읽을 때, string 타입의 key 사용을 허용하지 않으며, 이럴 때는 string literal 타입으로 사용해야 합니다!!

const obj = {
  foo: 'hello',
};

let propertyName1 = 'foo';
console.log(obj[propertyName1]); // error

const propertyName2 = 'foo';
console.log(obj[propertyName2]); // hello
console.log(obj['foo']); // hello
  • propertyName1은 string타입이므로 error
  • propertyName2 , ‘foo’ 자체는 string literal 이므로 제대로 출력이 되는 것이다

이처럼 string키로 객체에 접근하지 못하는 것은 여러모로 불편하다. 다음과 같이 Object.keys() 에서 리턴되는 값은 string[] 이기 때문에

JS에서 사용하던 코드를 그대로 사용하면 컴파일 에러가 발생한다.

for (const key of Object.keys(obj)) {
  console.log(obj[key]) // error(key가 string타입)
}

해결책으로는

  1. type assertion

    const obj = {
      foo: 'hello',
    };
    
    let propertyName1 = 'foo';
    console.log(obj[propertyName1 as 'foo']);
  2. index signature


참고 :

TypeScript에서 string key로 객체에 접근하기

profile
할 수 있다

0개의 댓글