- JSX 파일을 TSX확장자로 바꾸어 작업을 하면서 객체를 활용하고 프로퍼티에 접근, 프로퍼티를 추가하는데에 자바스크립트와 타입스크립트 간에 차이가 있다는 것을 새롭게 알게되어 아래에 정리해보고자 한다.
- 먼저, 타입스크립트 오류 메시지 중 읽어봐도 감이 안잡혔던 오류를 해결한 방법을 통해 객체 활용 시 자바스크립트와 타입스크립트의 차이점에 대해 말하면 좋을 것 같다.
오류 메시지 Element implicitly has an 'any' type because expression of type 'string' can't be used to index type 'ILanguageSvgObj'. No index signature with a parameter of type 'string' was found on type 'ILanguageSvgObj'. TS7053
TypeScript는 기본적으로 객체의 프로퍼티를 읽을 때,
string타입의 key 사용을 허용하지 않는다.
반드시 string literal 타입의 key로 접근하거나 객체의 타입 정의 시, 아래에서 설명할 index signiture를 선언해주어야한다.
🍀
const a = "Hello World"
// ⭐ 컴파일러는 이 변수를 string이 아닌 조금 더 좁은 타입으로 선언한 것으로 추론한다.(Literal Narrowing)
// ⭐ a의 타입은 string 타입보다 훨씬 구체적인 "Hello World" 타입이다.
let b = "Hello World"
// ⭐ b변수는 let으로 선언되어 재할당될 수 있을 경우 어떤 문자열이든 넣을 수 있으며 그 경우의 수가 무한대
// ⭐ 그렇기 때문에 컴파일러는 이 변수를 string타입으로 추론한다.
const c: string = "Hello World"
//c 변수는 명시적으로 string 으로 선언했으므로 string 타입이다.
🍀
const obj = {
foo: "hello"
}
let propertyName = "foo" //propertyName는 string 타입(let)
console.log(obj[propertyName])
// 💣💣 컴파일 에러!
//에러가 발생한 이유는 string literal 타입만 허용되는 곳(객체의 key)에 string 타입을 사용했기 때문
🍀
const obj = {
foo: "hello",
}
const propertyName = "foo"
console.log(obj[propertyName]) // ok!
console.log(obj["foo"]) // ok!
// ⭐⭐ 정상 컴파일
//⭐⭐ "foo"와 propertyName 모두 string literal type
String Literal타입은 열거형 타입처럼 사용할 때 매우 유용하다.
마우스 이벤트가 있다고 가정하고, 이벤트 종류는 정해져있지만 이벤트 이름을String 타입
으로 받을 때는 오타 혹은 유효하지 않은 이벤트 이름으로 인해 발생하는 런타임 에러를 사전에 방지 할 수 없다.
<function handleEvent(event:string) {} // 이벤트 이름을 string 타입으로 받는다면
handleEvent("clock")
handleEvent("click") //compile error : 오타. 컴파일 타임에 발견할 수 없다.
handleEvent("hover") //compile error : 유효하지 않은 이벤트 이름. 컴파일 타임에 발견할 수 없다
반면,
string literal
타입 조합만을 허용하도록 하면 사전에 오타 혹은 여러가지 에러를 방지할 수 있다.
type EventType = "mouseout" | "mouseover" | "click" //string literal 타입들의 조합
function handleEvent(event:EventType) {} //string literal 타입들의 조합을 이벤트(매개변수)의 타입으로 선언한다.
handleEvent("click")
handleEvent("hover") //compile error : EventType이라는 타입에 해당 string literal이 정의되지 않았기 떄문
당연히 방법은 있다~!
객체의 타입을 지정할 때 아래 처럼 index signature
를 한 줄 추가해주면
객체의 프로퍼티를 읽을 때 key값에 string literal 타입
이 아닌 string 타입
이 들어가도 컴파일이 가능하다.
const Svg = ({ name, hoverIcon }: Props) => {
interface ISvgObj {
[key: string]: () => any; // ⭐⭐⭐ index signature
}
const SvgObj: ISvgObj = {
C: () => (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 128 128"
width="current"
height="current"
>
<path
fill="current"
d="M117.5 33.5l.3-.2c-.6-1.1-1.5-2.1-2.4-2.6l-48.3-27.8c-.8-.5-1.9-.7-3.1-.7-1.2 0-2.3.3-3.1.7l-48 27.9c-1.7 1-2.9 3.5-2.9 5.4v55.7c0 1.1.2 2.3.9 3.4l-.2.1c.5.8 1.2 1.5 1.9 1.9l48.2 27.9c.8.5 1.9.7 3.1.7 1.2 0 2.3-.3 3.1-.7l48-27.9c1.7-1 2.9-3.5 2.9-5.4v-55.8c.1-.8 0-1.7-.4-2.6zm-53.5 55c9.1 0 17.1-5 21.3-12.4l12.9 7.6c-6.8 11.8-19.6 19.8-34.2 19.8-21.8 0-39.5-17.7-39.5-39.5s17.7-39.5 39.5-39.5c14.7 0 27.5 8.1 34.3 20l-13 7.5c-4.2-7.5-12.2-12.5-21.3-12.5-13.5 0-24.5 11-24.5 24.5s11 24.5 24.5 24.5z"
/>
</svg>
)
}
return (
<SvgContainer hover={name === hoverIcon}>
{SvgObj[name]()}
// ⭐⭐⭐ name(string 타입)이라는 변수를 key값으로 객체의 프로퍼티에 접근
// ⭐⭐⭐ 객체의 타입을 정의할 때 index signature를 선언해주었기 때문에 string 타입이어도 컴파일이 가능
</SvgContainer>
)
}
추가적으로 객체에 프로퍼티를 추가하는 것에 대한 이야기를 잠깐하고 마무리하겠다.
스트링(‘ ’) / 변수명 / 숫자
로 접근 할 수 있고, 점 표기범 (dot-notation) 이나 괄호 표기법 (bracket-notation)
둘다 지원한다. ( 단, number일 경우에는 괄호 표기법은 지원하지 않는다)obj[age] = 27
에서의 age 처럼 key에 number도 string도 아닌 값이 들어온다면 JS는 런타임 이전에 toString()
을 암묵적으로 호출해서 obj['age'] = 27
로 만든다.key type
은 무조건 string 혹은 number
이어야 하는 것이다.어떤 key type을 받을 것인지
도 정해줘야한다.const person = {};
person[name] = 'Yeonju' // ⭐ error : js와 다르게 name에 toString을 암묵적으로 호출하지 않는다.
person["name"] = 'Yeonju' // ⭐ error : 어떤 key Type을 Object가 받을 것인지 명시해줘야한다.
const foo : string = 'foo';
person[foo] = 'hello' // ⭐ error : 어떤 key Type을 Object가 받을 것인지 명시해줘야한다.
interface IPerson {
[key: string]: string;
}
const person: IPerson = {};
person['name'] = 'Yeonju'; //ok!!
const foo: string = 'foo';
person[foo] = 'hello'; //ok!!
참고 글
https://velog.io/@dongdong98/index-Signature-동적으로-Object에-property-추가하기
https://soopdop.github.io/2020/12/01/index-signatures-in-typescript/
덕분에 잘보고갑니다!