[TS] TypeScript와 Object

브리셀·2022년 1월 27일
0

JavaScript

목록 보기
8/8
post-custom-banner

개념정리 위주 보다는 마주한 문제를 해결하는 흐름으로 적어보았습니다.

처음 짠 코드

  • object를 export하는 외부 js 모듈로부터 새로운 배열을 만들어 반환하는 함수를 만들고자 했다.

    // jsObject.js
    export default {
     1: '문자열1',
     a: '문자열a'
    };
    // *.ts
    import jsObject from './jsObject.js';
    function toArray() : Array<Object> {
     const result : Array<Object> = [];
     Object.keys(jsObject)
       .forEach(k => result.push({
         name: k,
         desc: jsObject[k]
       }));
     return result;
  • 그랬더니 다음과 같은 오류가 났다.

    Element implicitly has an 'any' type
    because expression of type 'string'
    can't be used to index type '{ 1: string; a: string; }'.
    
    No index signature with a parameter of type 'string'
    was found on type '{ 1: string; a: string; }'.
  • 번역해보자면

    • 원소의 타입이 any로 추론되었는데(의역)
      string 타입 표현(식)은 { 1: string; a: string; } 타입의 인덱스로 사용될 수 없기 때문이다.
    • { 1: string; a: string; } 타입에서는
      string 타입의 파라미터를 갖는 index signature가 발견되지 않았다.
  • ... 이게 대체 무슨 소리야?<

우선 index signature가 뭔지 알아보자

  • 일단 TypeScript의 interface를 알고 있어야 한다.

    • C# 등 다른 언어의 interface와는 다소 다른 것 같다.
      (비슷할 수도 있다 ― 그러니까 같은 이름이 붙었겠지?)

    • 말하자면 object에 들어가는 key값과, value의 자료형을 미리 지정해두는 것이다.

      interface MyObject {
        name: string,
        desc: string
      }
      
      const myObject : MyObject = {
        name: 1, // string 타입이 아니므로 오류 발생
        /* desc: '인터페이스 예시', */ // 필요한 데이터쌍이 없어서 오류 발생
        etc: '기타 정보' // 인터페이스에 정의되지 않은 형태이므로 오류 발생
      };
    • 같은 방식으로 key값(=index)의 자료형도 지정해줄 수 있는데, 그 key값의 자료형을 index signature라고 한다.

      interface IndexSignature {
        [index: number]: string,
        [index: string]: number
        // 위와 같이 인덱스 타입에 따라 저장되는 자료형을 달리할 수도 있다.
      }
      
      const isObject : IndexSignature = {
        1: '예시',
        '1': 13
        // 이런 짓을 하시면 안 됩니다<
      }
    • 그러니까 내가 마주한 오류의 아랫줄은, [index: string]: string 형태로 정의된 항목이 없기 때문에
      string 타입(Object.keys()string[]을 반환한다고 함)으로 index에 접근할 수 없다는 의미이다.

어라, 'a'string 아닌가?

  • 오류 메시지를 다시 보니 object의 형태가 { string: string; } 형태가 아니고
    { 1: string; a: string; }이라고 되어있다.
    (구분자가 ,가 아니라 ;으로 쓰인 데에는 어떤 이유가 있겠지만 일단 넘어가자)
    • 그리고 "a"'a'도 아니고, 따옴표 없이 그냥 a라고 되어있다.
  • 찾아보니, TypeScript의 object key는 기본적으로 string literal 타입이라고 한다.
    • 흔히 말하는 "리터럴리 literally"의 그 literal이다.
    • 진짜 문자 그대로의 값만 허용되는, 그래서 동적으로 아무 값이나 허용되는 string과는 다른, string의 특수한 형태인 것이다.
      • 문자열 상수라고 생각하면 될지도..?
  • 그러니까, jsObject.js 파일에서 import 해온 저 jsObject에 대해서, TypeScript는 그 type(오류 메시지에 그렇게 써있다)을 { 1: string; a: string; }으로 추론한 것이고,
    그래서 저 object가 가질 수 있는 key값은 일반적인 string이 아니라 string literal1a 뿐이므로,
    그 외의 값으로 index에 접근하려하면, 접근 가능한 index 형태가 아니라는 오류를 내는 것이다!

그러면 key 값을 numberstring으로 혼용하지 말고, string으로 한정한다면 오류가 해결되려나?

  • 앞서도 언급했지만 기본적으로 TypeScript object의 key값은 string literal이므로, number형을 제한다고 한들 TypeScript가 jsObject{ string: string; } 형태로 인지하지는 않을 것이다.
  • 그리고 정말 외부에서 가져온 JavaScript 모듈이라면 내가 직접 수정한다는 것은 사실상 불가능한 선택지이다. 뭔가 다른 방법을 생각해야 한다.

index로 접근하지 않으면?

  • 굳이 Object.keys()로 object를 분해해야할 이유가 없다.
    Object.entires()를 쓰면 key와 value 모두 다룰 수 있는데!
  • 내친김에 interface도 활용해보았다.
    interface MyObject {
     name: string,
     desc: string
    }
    
    function toArray() : Array<MyObject> {
      const result : Array<MyObject> = [];
      for (const [key, value] of Object.entries(myObject)) {
        result.push({ name: key, desc: value });
      }
      return result;
    }
  • 아무 문제 없이 잘 작동했다. (약간 허무할 정도..)

이 문제는 해결 되었지만...

  • 만약 비슷한 배경에서, object의 형태를 바꾸지 않고, string으로 index에 접근해야 하는 상황이 생긴다면?
  • 위와 비슷 방식으로, index signature를 포함하는 interface를 선언하여 이식하면 될 것 같다.
    interface MyObject {
      [index: string]: string
    }
    
    function example () : MyObject {
      const result : MyObject = {};
      for (const [key, value] of Object.entries(description)) {
        result[key] = value;
      }
      return result;
    }
  • 작동 자체는 잘 하는데, 효율적인 방식은 아닌 것 같다...
  • object 형식이 복잡해지면 적용할 수 없을 수도 있겠지만 아직까진 떠오르는 상황이 없으므로 일단 여기에서 마치기로.

정리

  • TypeScript의 object의 key값(=index)은 기본적으로 string literal이다.
  • index를 string으로 접근해야만 한다면 interface를 이용하여 index signature를 바꿔줄 수 있다.
  • key만 가지고 용쓰지 말고 Object.entries()를 적극 활용하자.

참고한곳

profile
풀스택도 프론트부터
post-custom-banner

0개의 댓글