개념정리 위주 보다는 마주한 문제를 해결하는 흐름으로 적어보았습니다.
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가 발견되지 않았다.... 이게 대체 무슨 소리야?<
일단 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
아닌가?{ string: string; }
형태가 아니고{ 1: string; a: string; }
이라고 되어있다.,
가 아니라 ;
으로 쓰인 데에는 어떤 이유가 있겠지만 일단 넘어가자)"a"
나 'a'
도 아니고, 따옴표 없이 그냥 a
라고 되어있다.string literal
타입이라고 한다.string
과는 다른, string
의 특수한 형태인 것이다.jsObject.js
파일에서 import
해온 저 jsObject
에 대해서, TypeScript는 그 type(오류 메시지에 그렇게 써있다)을 { 1: string; a: string; }
으로 추론한 것이고,string
이 아니라 string literal
인 1
과 a
뿐이므로,number
와 string
으로 혼용하지 말고, string
으로 한정한다면 오류가 해결되려나?string literal
이므로, number
형을 제한다고 한들 TypeScript가 jsObject
를 { string: string; }
형태로 인지하지는 않을 것이다.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;
}
string
으로 index에 접근해야 하는 상황이 생긴다면?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
의 key값(=index)은 기본적으로 string literal
이다.string
으로 접근해야만 한다면 interface
를 이용하여 index signature를 바꿔줄 수 있다.Object.entries()
를 적극 활용하자.