『타입스크립트 교과서』의 후반부에는 자주 사용되는 패키지의 타입의 정의를 살펴보고 직접 타이핑해보는 과정이 담겨있다. 특정 패키지의 타입이나 직접 구현하는 내용보다는 그 과정에서 몰랐던 내용 위주로 정리하였다.
타입스크립트가 아닌 자바스크립트에 대한 내용은 소제목에 (js)
라고 표기하였다.
instrinsic
타입은 내부적으로 따로 구현되어 있다는 뜻이다. ex) Uppercase
interface Array<T> {
forEach(callbackfn: (value: T, index: number, array: T[]) => void, thisArg?: any): void;
}
배열 메서드(forEach
, map
, reduce
등)의 두번째 인자로 this를 정할 수 있다.
Promise 객체
와 Promise 인스턴스
는 다른 개념이다.
Promise.resolve
에서의 Promise
가 Promise 객체
이고,
new Promise()
나 Promise.resolve()
의 반환 값이 Promise 인스턴스
이다.
//v5.0.4
interface CallableFunction extends Function {
...
bind<T, A0, A extends any[], R>(this: (this: T, arg0: A0, ...args: A) => R, thisArg: T, arg0: A0): (...args: A) => R;
bind<T, A0, A1, A extends any[], R>(this: (this: T, arg0: A0, arg1: A1, ...args: A) => R, thisArg: T, arg0: A0, arg1: A1): (...args: A) => R;
bind<T, A0, A1, A2, A extends any[], R>(this: (this: T, arg0: A0, arg1: A1, arg2: A2, ...args: A) => R, thisArg: T, arg0: A0, arg1: A1, arg2: A2): (...args: A) => R;
bind<T, A0, A1, A2, A3, A extends any[], R>(this: (this: T, arg0: A0, arg1: A1, arg2: A2, arg3: A3, ...args: A) => R, thisArg: T, arg0: A0, arg1: A1, arg2: A2, arg3: A3): (...args: A) => R;
bind<T, AX, R>(this: (this: T, ...args: AX[]) => R, thisArg: T, ...args: AX[]): (...args: AX[]) => R;
}
interface NewableFunction extends Function {
...
bind<A0, A extends any[], R>(this: new (arg0: A0, ...args: A) => R, thisArg: any, arg0: A0): new (...args: A) => R;
bind<A0, A1, A extends any[], R>(this: new (arg0: A0, arg1: A1, ...args: A) => R, thisArg: any, arg0: A0, arg1: A1): new (...args: A) => R;
bind<A0, A1, A2, A extends any[], R>(this: new (arg0: A0, arg1: A1, arg2: A2, ...args: A) => R, thisArg: any, arg0: A0, arg1: A1, arg2: A2): new (...args: A) => R;
bind<A0, A1, A2, A3, A extends any[], R>(this: new (arg0: A0, arg1: A1, arg2: A2, arg3: A3, ...args: A) => R, thisArg: any, arg0: A0, arg1: A1, arg2: A2, arg3: A3): new (...args: A) => R;
bind<AX, R>(this: new (...args: AX[]) => R, thisArg: any, ...args: AX[]): new (...args: AX[]) => R;
}
bind의 인수 개수는 무한할 수 있기 때문에 인수 4개인 경우까지의 타입만 구현되어 있다.
//v5.1.6
interface CallableFunction extends Function {
...
bind<T, A extends any[], B extends any[], R>(this: (this: T, ...args: [...A, ...B]) => R, thisArg: T, ...args: A): (...args: B) => R;
}
interface NewableFunction extends Function {
...
bind<A extends any[], B extends any[], R>(this: new (...args: [...A, ...B]) => R, thisArg: any, ...args: A): new (...args: B) => R;
}
실제로 확인해보니 v5.1.6
부터는 타입이 변경되어서 인수 개수가 많아져도 정확한 타입이 구현되도록 변경되었다.
타입스크립트는 타입스크립트 코드의 에러를 검사하는 작업과 타입스크립트 코드를 자바스크립트 코드로 변환하는 작업을 독립적으로 수행하기 때문에 타입에러와 관계없이 자바스크립트 변환작업은 수행된다.
$ npx tsc -noEmitOnError
에러가 없을 시에만 변환작업 수행하는 옵션
1. 현재 파일이 있는 폴더에 node_modules가 있는지 확인하고 있으면 2번부터 12번까지 순차적으로 파일을 찾을 때까지 수행, 없으면 부모 폴더로 올라가서 다시 1번을 수행
2. node_modules/module.ts
3. node_modules/module.tsx
4. node_modules/module.d.ts
5. node_modules/module/package.json 속성 찾기
6. node_modules/module/index.ts
7. node_modules/module/index.tsx
8. node_modules/module/index.d.ts
9. node_modules/@types/module/package.json 속성 찾기
10. node_modules/@types/module.d.ts
11. node_modules/@types/module/index.d.ts
12. 2~11번까지 모두 못찾았으면 부모 폴더로 올라가서 1번을 수행
13. 최상위 폴더까지 갔는데도 못 찾으면 에러
declare global
는 모듈 파일 안에서 전역 타입을 만드는 선언 방식이다.
declare module
는 타입스크립트에게 해당 모듈이 있다는 걸 알리고, 해당 모듈에 대한 타입 선언도 이 블록 안에 있음을 알리는 선언이다.
같은 이름의 ts
파일과 d.ts
파일이 같은 폴더에 존재하면 타입스크립트는 ts
파일만 인식한다.
직접 패키지를 타이핑할때 declare module
내부에 있는 import
를 declare module
외부로 빼면 안된다.
=> 기존 모듈을 새로 선언하게 되므로
declare module
을 사용하지 않고 tsconfig.json
의 baseUrl
과 paths
옵션으로 타입의 경로를 지정하는 방법도 있다.
tsconfig.json
의 rootDir
의 기본값은 tsconfig.json
이 위치한 경로가 아니고 ts
파일(d.ts
파일은 무시)이 위치한 가장 얕은 경로이다.
책의 후반부에는 타입을 찾아서 이해하는 과정과 에러가 발생하지 않도록 직접 타입을 구현하는 방법에 대한 이해가 필요한 파트여서 정리할 내용이 많지 않았다. 이미 구현되어 있는 패키지의 타입을 이해하고 어떤 방법으로 정의되어 있는지 보는 것이 실무에서 타입 정의를 하는데 도움이 된다고 느꼈다.