React Master코스를 노마드에서 수강하는데 TS를 알려줬다. TS를 배울 생각이 있지는 않았는데 요즘 추세도 그렇고 오랜만에 강타입 언어를 배워보고 싶었기 때문에 재밌게 배웠다. 하지만 거기서는 자세하게 가르쳐 주지는 않았다. TS로 리액트를 쓸 건데 이런 부분이 필요하다 정도를 간단히 배웠다. 하지만 그걸로는 부족했다. 그래서 강의를 듣는 중간에도 '왜 이렇게 될까?'하는 생각이 많이 들었다. 찾아보기도 했지만 파편적으로 찾아보다 보니 이해가 더 안되는 느낌이 강했다.
그러던 와중에 희정님이랑 TS 이야기를 하게 되었다. '님도.. TS 공부함? 그럼 스터디??'하고 순식간에 스터디를 하기로 했다. 한 주 정도 했는데 배운 게 제네릭과 다형성, type과 interface의 차이가 있다. 오늘은 굵직한 내용을 중심으로 해서 한 주간 공부를 되돌아 볼까 한다.
이번에 스터디를 하면서 다형성에 대해서 배웠다. 다형성이라는 개념을 알게 된 것은 이번이 처음이었다. 영어로 하면 Polymorphism인데, 즉 다양한 형태를 말한다. 프로그래밍에서 다형성은 하나의 코드를 다양한 형태로 사용하는 것을 말한다. 아래 예시 코드에서 확인해보자.
// 제네릭으로 타입
type Add = <T>(a: T[]) => T;
const add: Add = (arr) => {
arr.forEach((el) => console.log(el));
return arr[0];
};
// 제네릭은 사용자가 type을 입력할 수 있도록 허용한다. 그래서 아래와 같이 입력을 하면 number | string | boolean으로 타입된다.
add([1, "string"]);
아래는 typescript playground에서 위 코드를 작성한 후 add의 call signature를 확인한 결과이다.
Add를 제네릭으로 타입했기 때문에 typescript가 타입을 추론할 수 있고, 이를 통해서 다형성을 구현했다. 그리고 다형성을 구현한 코드이기 때문에 각각의 상황에서 다양하게 사용할 수 있다.
이쯤되면 제네릭에 대해 궁금증이 생길 것이다.
type Add = (a:Array<string|number>) => string|number|boolean;
const add:Add = (arr)=>{
arr.forEach((el)=>console.log(el));
return arr[0];
}
add([1,"string"]);
위 코드는 제네릭을 써 타입한 Add함수를 제네릭을 쓰지 않고 타입한 코드이다. 만약 여기서 add함수의 세 번째 배열 인자로 true라는 boolean을 넣으면 타입 에러가 발생한다.
왜냐하면 파라미터 타입 중 boolean이 없기 때문이다. 세 번째 인자로 true를 사용하기 위해서는 타입을 추가해주어야 한다. 하지만 제네릭은 이런 번거로운 부분이 없다. 위에서 말했듯이 typescript가 타입을 추론하기 때문이다.
Any는 typescript의 보호를 받을 수 없다는 뜻이다. 어떤(any) 타입도 들어올 수 있다는 시멘틱도 있지만 어떤 보호도 받을 수 없다는 표현이 훨씬 정확하다고 생각한다.
any로 타입을 하면 call signature부터가 다르게 나온다. 즉 제네릭으로 타입했을 때는 타입을 추론하기 때문에 추론된 타입을 바탕으로 보호받을 수 있지만 any로 타입을 하면 어떠한 보호도 받을 수 없다는 차이가 있다.
두 가지는 비슷하면서도 다른 점이 확실하다. 아래 내용에서 두 가지를 비교해보도록 하자.
먼저, type은 다재다능한 친구이다. Alias처럼 쓸 수도 있고 객체를 타입할 수도 있고 개별적인 value를 타입할 수도 있다. 이 세 가지 경우를 아래 예시 코드로 확인해보자.
type Player = string; // string을 Player로 alias
type Team = { // 객체를 타입
member: string[]
}
type Support = "Grazie" // 특정 value로 타입
하지만 interface는 객체를 타입하는 용도로 사용할 수 있다.
interface Team {
player: string,
league: string[]
}
또 다른 점으로는 중복선언이 되는지 여부이다. type은 중복선언이 안된다. 하지만 interface는 중복선언을 하면 이전에 선언해 둔 내용에서 추가가 된다. 내용이 축적되는 것이다.
상속할 때의 시멘틱도 다르다. type은 &연산자를 통해서 상속을 나타내지만 interface는 extends를 사용한다.
그렇게 생각할 수 있다. 그리고 어느 정도는 맞는 말이라고 본다. 하지만 시멘틱적으로 상속 시에 extends 키워드를 사용하기 때문에 interface가 더 객체지향에 적합하다고 생각한다. 그래서 class를 타입하기 위해서 추상 클래스를 만들 수 있지만 interface를 통해서 하면 시멘틱적으로 이질감이 없으면서도 js로 컴파일 시 깔끔함을 유지할 수 있다.
타입스크립트는 자바스크립트의 슈퍼셋 프로그래밍 언어답게 생산성과 안정성을 높여준다는 느낌을 받았다. 특히나 타입된 결과를 바탕으로한 안정적인 auto-complete는 강한 인상을 주었다. 아직은 걸음마 단계이지만, 지금까지 배운 걸 바탕으로라도 react에도 적용시키면 재밌겠다는 생각을 했다.