Programming/JavaScript

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

 

항해플러스 FE 2기 참여해서 사전스터디로 자바스크립트와 타입스크립트 기초 강의를 받았다.

타입스크립트 2주차 강의에서 pure javascript의 배열 내장함수를 사용하면 하드코딩하지 않아도 될 부분이 있어

평소 하던대로 작성해보았는데 타입 에러가 발생했다.

 

기능  : Student 타입의 객체인 학생의 각 과목 접수를 합산해서 평균을 내는 함수

 

▼강의 내 정답 함수 코드

interface Student {
  name: string;
  age: number;
  scores: {
    korean: number;
    math: number;
    society: number;
    science: number;
    english: number;
  };
}

function calculateAverage(student: Student): number {
  const sum = student.scores.korean + student.scores.math + student.scores.society + student.scores.science + student.scores.english;
  const average = sum / 5;
  return average;
}

 

▼ 내가 작성한 코드

function calculateAverage(student: Student): number {
  const keys = Object.keys(student.scores);
  let sum = 0;

  keys.forEach((e) => {
    sum += student.scores[e];  // 이 부분에서 error 발생
  });

  return sum / keys.length;
}

 

▼ compile error 내용

 

Element implicitly has an 'any' type because expression of type 'string' can't be used to index type '{ korean: number; math: number; society: number; science: number; english: number; }'.
No index signature with a parameter of type 'string' was found on type '{ korean: number; math: number; society: number; science: number; english: number; }'.
ts(7053)

 

sum += student.scores[e];

 

pure javascript를 사용했을 때 아무런 문제가 없던 부분인데 왜 타입스크립트에서는 타입 불일치 에러가 발생했을까?

 

문제는 바로! 인터페이스로 선언한 Student 타입에서 key 값에 대한 타입 선언을 하지 않았기 때문이다.

객체의 key에 대해 타입 선언을 하지 않는다면 String literal 타입으로 String 타입보다 좁은 의미의 타입으로 선언된다.

 

예를 들면,

 

type obj = {
    name : string
    age : number
}

 

위와 같이 obj 라는 타입은 name, age를 key로 가지고 있는데

해당 key의 각각의 타입 또한 name, age 로 String literal 타입이다.

문자열의 형태를 가지고 있다고 해서 string 타입이 아니고 값까지 타입 결정의 일부가 되는 것이다.

 

이와 같이 string literal 타입이 아닌 string 타입으로 해석하고 싶다면, index signature를 사용하면 된다.

type obj = {
  [index: string]: string
  name: string
  age: string
}

 

index signature 란 타입의 속성(키, 프로퍼티) - 값 페어의 타입을 선언해두는 것이다.

key의 타입이 다른 index signature는 여럿 선언할 수 있지만, index signature의 value type은 모두 일치해야한다.

 

interface Student {
  name: string;
  age: number;
  scores: {
    [key: string]: number;
    korean: number;
    math: number;
    society: number;
    science: number;
    english: number;
  };
}

function calculateAverage(student: Student): number {
  const keys = Object.keys(student.scores);
  let sum = 0;

  keys.forEach((e) => {
    sum += student.scores[e];  // ok
  });

  return sum / keys.length;
}

 

 

이렇게 index signature를 사용하면 동적으로 key를 변경하여 객체 내의 값의 가져올 때 타입 불일치 에러를 피할 수 있다.

 

 

참고 

https://soopdop.github.io/2020/12/01/index-signatures-in-typescript/#index-signature-%EC%84%A0%EC%96%B8%ED%95%98%EA%B8%B0