프로토타입 안전하게 확장하기
예전엔 프로토타입 확장이 안전하지 않은 일이었지만 이제 TypeScript처럼 정적 타입 시스템을 이용하면 안전하게 확장할 수 있습니다.
예시를 위해 Array 프로토타입에 zip 메서드를 추가해 보겠습니다.
프로토타입을 안전하게 확장하기 위해 두 단계로 진행할 것입니다.
먼저 .ts 파일에서 Array의 프로토타입을 확장한 다음 새로운 zip 메서드를 프로토타입에 추가합니다.
// TS에 zip이 무엇인지 설명
interface Array<T>{ // ①
zip<U>(list: U[]): [T, U][]
}
// .zip 구현
Array.prototype.zip = function<T, U> (
this: T[], // ②
list: U[]
): [T, U][] {
return this.map((v, k)=>
tuple(v, list[k]) // ③
)
}
1. 우선 TypeScript에게 zip을 Array에 추가하도록 지시했습니다. 인터페이스 합치기 기능을 이용해 전역 범위로 정의된 Array<T> 인터페이스에 zip 메서드를 추가했습니다.
파일에서 임포트(import)나 익스폴트(export)를 명시하지 않았으므로 기존의 전역 인터페이스와 같은 이름인 Array<T> 인터페이스를 직접 선언할 수 있었고 TypeScript는 자동으로 둘을 합쳐줍니다.
파일이 모듈 모드라면 전역 확장을 declare global이라는 타입 선언으로 감싸야합니다.
declare global{
interface Array<T>{ // ①
zip<U>(list: U[]): [T, U][]
}
}
global은 전역으로 정의된 모든 값을 포함하는 특별한 네임스페이스로, 이를 이용하면 모듈 모드의 파일에서 전역 범위에 존재하는 이름들도 확장할 수 있게 됩니다.
2. 그리고 Array의 프로토타입에 zip 메서드를 구현했습니다.
this 타입을 사용하여 TypeScript가 .zip이 호출되는 대상 배열에서 T 타입을 올바로 추론할 수 있도록 했습니다.
3. TypeScript는 이 매핑 함수의 반환 타입을 (T | U)[]로 추론하므로, 타입 어서션 없이 튜플을 만들기 위해 앞서 소개한 tuple 유틸리티를 이용했습니다.
interface Array<T>를 선언할 때 전역 Array 네임스페이스에 추가했습니다.
즉, 다른 파일에서 zip.ts를 임포트 하지 않아도 [].zip을 이용할 수 있으리라 짐작됩니다.
하지만 Array.prototype에 기능을 추가하려면 zip을 사용하는 모든 파일이 zip.ts를 먼저 로드해야 합니다.
프로젝트에서 zip.ts를 명시적으로 제외하도록 tsconfig.json을 수정합니다.
그러면 이 기능을 사용하는 쪽에서 명시적으로 임포트해야 합니다.
{
*exclude*: [
"./zip.ts"
]
}
그러면 다음처럼 안전하게 zip을 사용할 수 있습니다.
import './zip'
[1, 2, 3]
.map(n => n *2) // number[]
.zip(['a', 'b', 'c']) // [number, string]
위의 코드를 실행하면, 배열에 매핑과 압축(zip)을 차례로 실행한 결과를 얻을 수 있습니다.
import './zip'
[1, 2, 3]
.map(n => n *2) // number[]
.zip(['a', 'b', 'c']) // [number, string]
[
[2, 'a'],
[4, 'b'],
[6, 'c']
]
'👶 TypeScript' 카테고리의 다른 글
에러 처리 - 예외 던지기 (0) | 2023.01.23 |
---|---|
에러 처리 - null 반환 (0) | 2023.01.22 |
이름 기반 타입 흉내내기 (0) | 2023.01.22 |
탈출구 - 확실한 할당 어서션 (0) | 2023.01.22 |
탈출구 - Nonnull 어서션 (0) | 2023.01.22 |