좋은 기회가 생겨서 TypeScript 의 핸드북을 정독하게 되었고
그동안 인터넷 검색으로 어렴풋이 알고 있었던 TypeScript 사용법을 이번기회에 더욱 명확하게 알 수 있을 것 같다.
해당 글은 그 동안 놓쳤던 부분들을 정리하는 느낌으로 글을 작성하려고 합니다!
전체 8가지 항목으로 내용이 생각보다 길다.
이 중 읽으면서 인상 깊었던 파트는 아래와 같다
하나씩 정리해보면
기초적인 내용 중 다운 레벨링이라는 용어가 새롭게 들렸다.
컴파일 하기 전 해당 코드를 컴파일 하게 된다면
// test.ts
`Hello ${person}, today is ${date.toDateString()}!`;
아래와 같이 변환 되는데
// test.js
"Hello " + person + ", today is " + date.toDateString() + "!";
이유는 다운 레벨링이라고 하여 컴파일 이후 나오는 JS 문법은 Default 로 아주 구버전인 ES3에 문법에 맞추도록 되어있다.
이렇게 설정한 이유가 조금 놀라웠다.
물론 설정값으로 변환시 사용될 문법을 선택할 순 있지만 컴파일이후 이전에 사용한 모든 JS 도 동일하게 유지하기 위해 가장 구버전을 사용한 부분이 레거시 코드를 고려한 것 같아서 놀랐다
해당 항목에서는 자주 쓰는 Type 에 대해서 알려준다.
string, number, any, 등등
또한 이를 가지고 놀 수 있는 유니언 타입, 별칭, 인터페이스, 타입 등등을 알려준다.
여기서 놀랐던 부분은 인터페이스와 타입의 확실한 차이점을 알려주었다.
TypeScript 를 하다보면 inteface 로도 타입 명시가 가능하고 type 으로도 명시가 가능한데
이 2개의 차이점이 대체 뭐지?
이런 궁금증이 있었기 때문이다.
가장 큰 차이점은 인터페이스는 확장과 새로운 타입 선언이 가능하지만 타입은 그렇지 않다
// inteface 확장 -> 항상 확장만 가능
interface car {
name: string;
}
interface productCar extends car{
modelNum: number;
}
// type 확장 -> 개방이기 때문에 유지, 추가, 선택 가능
type car {
name: string;
}
type productCar = car & {
modelNum: number;
}
type anotherCar = car
type anotherNum = car | {
modelName: string;
}
// interface 수정 -> 변경이 가능해진다.
interface user {
name: string;
}
interface user {
nickName:string
}
> user.nickName 출력
// type 수정 -> 변경 불가능
type user = {
name: string;
}
type user = {
nickName: string // => ERROR!! Duplicate identifier 'user'.
}
이런 차이를 확인해 봤을 땐 개인적인 생각으로는 type 에 어울리는 문법은 역시 interface 보단 type 같다.
interface 는 interface 의 역할만 하는 것이 적당하다고 생각한다.
유니온 차별화…? 라고 되어있는데 TYPE 을 제한 할 수 있는 부분이다.
해당 파트에서 인상적이였던 부분은 일부 타입을 명시할 수 있다는 점이다.
예를 들어 content type 에는 NOTICE
, NORMAL 밖에 안들어간다고 했을 때 2가지의 타입 STRING 이기 때문에 오타 확인은 내부에서 일일이 해줘야한다.
// content type: NOTICE, NORMAL
type content = {
type: string
}
const content:content = new Content()
if(content.type !== 'NOTICE'){
throw new Error('error!')
} else if(content.type !== 'NORMAL'){
throw new Error('error!')
}
하지만 다음과 같이 string 자체를 type으로 명시할 수 있기 때문에 오타에 대한 부분을 덜 수 있다.
type content = {
type: 'NOTICE'| 'NORMAL'
}
const content:content = new Content()
content.type = 'REVIEW' // ERROR! : NOTICE| NORMAL 중 하나가 아님
이런 기능은 앞으로 더 많이 쓸 것 같아서 기억에 남는다
해당 부분에서 인상 깊었던 부분은 함수 부분이다
단순히 생각해봤을때 당연한 것 같아 작성하였던 부분인데
// function
function testFunction(a: any[]): any{ // 어째뜬 리스트 형태를 받아서
return a[0] // 리스트 첫번째 인수를 리턴
}
const a = testFunction([1, 2, 4]) // a: number 추론...?
any[] 로 표기를 했으니 당연히 리스트의 존재로 인식할 줄 알았고
이렇게 작성한다면 return type 으로는 input 에 따라 바뀌겠지 했었다
그렇지만 이렇게 한다 하더라도 any[] 는 여전히 any 로 표기를 할 수 있는 부분이고
input 에 따라 달라지다는 명시가 없으므로 어찌보면 당연한 거다.
input 에 따라 바뀌게 수정하고 싶다면 다음과 같이 작성해야한다.
// function
function testFunction<Type>(a: Type[]): Type | undefined{ // 어째뜬 리스트 형태를 받아서
return a[0] // 리스트 첫번째 인수를 리턴
}
const a = testFunction([1, 2, 4]) // a: number 확정!
type 을 확장시켜서 INPUT 을 제한하는 방법도 인상적이였다.
// function
function lengthCheck<targetType extends {length: number}>(target: targetType): number{
return target.length
}
const a = lengthCheck(3) // ERROR!: number is not has 'length'
// <> 내부에서 extends 를 활용하면 type 요소를 제한할 수 있다!
공식 문서를 이렇게 정독해보긴 처음이다.
보통 개발하다가 막히는 부분이 있다고 하면 해당 부분만 발췌하여 읽곤 했는데
한번 정독하는 것도 나쁘지 않은 방법같다. 새롭게 알게 된 내용도 있을 뿐더러
그동안 찾아보기 어렵거나 검색해도 나오질 않았던 부분이 명쾌하게 해결되는 부분도 있다는 것을 알았다.
다만 너무 내용이 지루할 순 있어서 적절히 조절하며 읽어야 겠다.