인프런 타입스크립트 기초를 통해 공부중이며 제로초님의 유료강좌입니다.
코드를 통으로 가져오지는 않겠습니다.(내가 글을 쓰면서 복습하는게 목적이기때문에 필요한 부분만)
타입스크립트에 익숙하지않다면 자바스크립트로 먼저 코드를 작성하고 그 후에 타입스크립트로 변환하도록하자.
타입스크립트 (Typescript)
클래스와 인터페이스를 사용할때 언제 써야할까?
만약 내가 작업할 때 new키워드를 쓰지 않고 상속의 구현만 할것이라면 인터페이스를 사용해도 무방하다.
(실제 사용은 클래스에서 extends 하는 방식으로 하고 구현만 할거라면 인터페이스 implements해준다.
interface Example{
(a: number, b: number) : number;
}
const ex: Example = (a,b) => a + b;
함수나 클래스는 할당되는 문법이 따로 있기 때문에 보통 객체에서 인터페이스를 많이 쓴다고 한다.(취향일수도)
인터페이스로 객체 타입을 선언
interface Example{
add: (a: number, b: number) => number;
}
const ex: Example = {
add: (a,b) => {
return a + b;
}
}
제네릭(Generic)
인터페이스에서의 제네릭
// 인터페이스
interface Test<T>{
//...
}
// 클래스
class Test<T> implements Example{
//...
}
// 함수
function Test<T>(arr: T[], callback: (item: T) => void): void{
//...
}
타입 여러개를 더하기하는 함수를 만들고 싶다고 했을때 어떻게 해야할까?
(아래 예제에 string 타입을 더하고 있는 함수하나가 있고 string과 number타입을 모두 포함하는 함수를 만들고 싶다. 그리고 같은 타입끼리만 더해져야한다.)
function add(a: string, b: string): string{
return a + b;
}
두번 선언할 것인가?
자바스크립트에서는 같은 함수를 두 번 선언할 수 없다.
// (X)
function add(a: string, b: string): string{
return a + b;
}
function add(a: number, b: number): number{
return a + b;
}
이렇게 선언한다면 에러가 난다. 그렇다면 or 연산자를 써서 해결해야할까?
function add(a: number | string, b: number | string): number | string{
return a + b;
}
타입 두개를 추가하는데에는 만족했지만 해당 타입끼리 더해야한다는 요구조건을 만족할 수 없다.
아래와 같이 string과 number의 더하기도 허용한다.
add(1 + 'abc');
이때 해결할 수 있는 방법은 제네릭을 사용하는것이다.
제네릭은 임의의 변수를 만들어서 함수를 생성하고 나중에 이 함수를 사용할때 타입값을 입력받는 것이다.
함수로는 생성할 수 없고 인터페이스를 통해 생성해야한다.
// (X)
function add<T>(a: T, b: T): T{
return a + b;
}
// (O)
interface obj<T>{
add: (a: T, b: T) => T;
}
인터페이스 선언 시에 T라는 임의의 타입을 작성해 둠으로써 여유롭게 선언한다.
(T는 임의의 이름이다. 다른 이름도 상관 없다)
// T라는 타입을 제네릭으로 바꾼다.
interface obj<T>{
add: (a: T, b: T) => T;
}
// 인터페이스를 사용할때 타입을 정해준다.
// <T>에 number를 넣게되는 것
const a: obj<number> = {
add: (a, b) => (a + b);
};
// <T>에 string을 넣게되는 것
const b: obj<string> = {
add: (a, b) => (a + b);
};
a.add(1, 2);
b.add('a', 'b');
addEventListener를 사용할 때 보통 아래와 같이 사용하는데,
타입스크립트가 어떤식으로 동작하는지 알기 위해 lib.dom.d.ts파일을 확인해보자.
document.addEvenListener<'submit'>('submit', () => {});
lib.dom.d.ts에는 <K extends keyof ...... 라고 적혀있다.
이때 저 K가 제네릭이다.
실제 내가 넣었던 'submit'이 K로 들어가게된다.
그리고 extends keyof DocumentEventMap을 확인한 뒤 DocumentEventMap -> ... -> 순서대로 따라가다보면 GlobalEventHandlerEventHandlersEventMap까지 다다를 수 있는데,
내부에 key를 확인해보면 된다.
key에 없는 값을 입력할 경우 타입스크립트에서는 유추할 수 없다.
만약 타입이 현저히 다를 경우 두번째 documentEventListener(type: string.... 선언에 맞춰간다.
(둘 중에 적합한 것을 찾는다)
이것을 function overloading이라고 한다.
제네릭에서 extends란
클래스에서 extends는 상속을 말하는데 타입스크립트에서 extends는 제한이라는 뜻이다.
즉 타입에 대한 제한을 두는 것이다.
아래 예시는 boolean을 타입으로 넣는 경우이다.
interface obj<T>{
add: (a: T, b: T) => T;
}
const a: obj<boolean> = {
add: (a, b) => (a + b);
};
boolean타입을 더한다니? 이렇듯 아무거나 타입을 넣게되면 원하는 결과가 나오지 않기 때문에 제한을 둔다.
만약 extends string을 넣는다면 string과 string을 extends한 타입들만 선언할 수 있도록 하는 것이다.
interface obj<T extends string>{
add: (a: T, b: T) => T;
}
여기서 number를 넣게되면 에러가 난다.
아래처럼 작성된 경우 K는 HTMLElementEventMap에서 keyof로 제한을 두겠다는 의미다.
첫번째 인수로는 key로 포함된 이벤트만 들어와야 에러가 나지 않는다.
document.addEventListener<'submit'>('submit',
제네릭에 기본 값을 넣을 수 있다.
// K가 넘겨오는 타입 extends는 제약사항
// HTMLElementTagNameMap에서 key를 제약으로둔다.
// = 'div'는 기본 값
createElement<K extends keyof HTMLElementTagNameMap = 'div'>(.....
+ 제네릭 안에 제네릭도 들어갈 수 있다.
lib.dom.d.ts파일의 find를 확인해보자.
이것은 인터페이스 내부에 들어있다.
아래와 같이 선언했을때
Array<T>에서 먼저 타입을 받고 있다.
그리고 Array로 'a', 'b', 'c'를 삽입했기 때문에 item의 타입이 string인 것을 타입스크립트는 유추할 수 있다.
['a','b','c'].forEach((item) => {});
만약 오만가지 타입을 다 넣는다면 any가 될까?
아니다.
// item : string | number | boolean | undefined | null 형식으로 유추한다.
['a',1,true, undefined, null].forEach((item) => {});
빈 배열을 삽입할 경우에는 never가 된다.
[].find((item) => {
//......
});
아래 find에는 predicate라는 텍스트가 있는데
true, false를 반환하는 콜백함수인 경우에 predicate라 부른다.
forEach를 구현하여 제네릭에 대해 더 자세히 알아보자.
function forEach<T>(arr: T[], callback: (item: T) => void): void{
for(let i: number = 0; i < arr.length; i++){
callback(arr[i]);
}
}
// 사용할때 제네릭 넣기
// item이 자동으로 유추된다.
// forEach 선언할 때 item의 타입에도 T를 넣었기 때문에 유추가 되는 부분이다.
forEach<string>(['a', 'b', 'c'], (item) => {
//........
});
일단 타입스크립트에서 타입추론되는 부분이 있다면 그 중에 제네릭일 확률이 있고 콜백함수에서 타입추론되는 경우 제네릭일 확률이 높다고한다.