이펙티브 타입스크립트 스터디 - 6주차 발표 정리
아이템 49. 콜백에서 this에 대한 타입 제공하기
let, const는 렉시컬 스코프인 반면에, this는 다이나믹 스코프이다.
이는 '정의된' 방식이 아니라 '호출된' 방식에 따라 달라지는 것을 말한다.
const c = new C()
const method = c.logSquares
method()
이 코드는 this를 찾지못해 런타임에 오류를 발생시킨다.
method.call(c)
이럴때는 call을 사용해서 명시적으로 this를 바인딩 하여 문제를 해결할 수 있다.
this는 반드시 인스턴스에 바인딩 되어야하는 것이 아니며 어떤 것이든 바인딩 할 수 있다.
class ResetButton {
constructor() {
this.onClick = this.onClick.bind(this)
}
onClick() {
alert(`Reset ${this}`)
}
}
콜백 함수에 쓰일때 위처럼 작성하면 this 바인딩 에러가 난다.
생성자에서 this 바인딩을 통해 해결할 수도 있지만,
onClick = () => {
alert(`Reset ${this}`)
}
위와 같이 화살표 함수로 선언해주면, 간단하게 this 바인딩 문제를 해결할 수 있다.
콜백함수 안에서 this 사용시에는 타입을 지정해주는 것이 좋다.
아이템 50. 오버로딩 타입보다는 조건부 타입을 사용하기
function double(x: number | string): number | string
function double(x: any) {
return x + x
};
const num = double(12); // string | number
const str = double('x'); // string | number
1. 제네릭 사용
function double<T extends number | string>(x: T): T
function double(x: any) {
return x + x
};
const num = double(12); // Type is 12
const str = double('x'); // Type is "x"
타입이 명확해진다.
하지만, 너무 구체적이다.
2. 타입 선언 분리
function double(x: number): number
function double(x: string): string
function f(x: number | string) {
return double(x)
// ~ 'string | number' 형식의 인수는
// 'string' 형식의 매개변수에 할당될 수 없습니다.
};
유니온 타입에서 문제가 생김
3. 조건부 타입 사용
function double<T extends number | string>(x: T): T extends string ? string : number
원하는대로 타입이 잘 동작한다.
조건부 타입은 추가적인 오버로딩 없이 유니온 타입을 지원할 수 있으므로, 오버로딩 타입보다 조건부 타입을 사용하는 것이 좋다.
아이템 51. 의존성 분리를 위해 미러 타입 사용하기
function parseCSV(contents: string | Buffer): { [column: string]: string }[] {
if (typeof contents === 'object') {
// It's a buffer
return parseCSV(contents.toString('utf8'))
};
...
};
Buffer는 Node.JS 사용자를 위한 타입 정의 (따로 설치를 해야한다.)
또한 @types/node에 의존하기 때문에 @types/node는 devDependencies로 포함해야 한다.
이로 인해 @types와 NodeJS와 무관한 개발자들이 이를 설치해야한다.
각자가 필요한 모듈만을 사용할 수 있도록 구조적 타이핑을 적용할 수 있다.
interface CsvBuffer {
toString(encoding: string): string
}
function parseCSV(contents: string | CsvBuffer): { [column: string]: string }[] {
return []
}
위처럼 @types/node의 Buffer 선언 없이 필요한 메서드 속성만 별도로 작성하여 사용할 수 있다.
아이템 52. 테스팅 타입의 함정에 주의하기
타입 선언을 테스트하기는 매우 어렵다.
단언문으로 타입선언을 때우는 것보다 dtslint 또는 타입 시스템 외부에서 타입을 검사하는 유사한 도구를 사용하는 것이 더 안전하고 간단하다.
test('square a number', () => {
square(1)
square(2)
});
런타임 동작을 테스트한다고 가정하면, 위 테스트 코드는 '실행'에서 오류가 발생하지 않는지만 체크하고 실행의 결과에 대해서는 테스트하지 않는다.
실제로 반환 타입을 체크하는 것이 훨씬 좋은 테스트 코드이다.
const lengths: number[] = map(['john', 'paul'], name => name.length)
특정 타입의 변수에 할당하여 반환타입을 체크할 수 있지만, 불필요한 변수를 선언해야한다.
이럴때는 헬퍼 함수를 정의해보자.
function assertType<T>(x: T) {}
assertType<number[]>(map(['john', 'paul'], name => name.length))
다만, 이것도 문제가 있다.
assertType<{ name: string }[]>(
map(beatles, name => ({
name,
inYellowSubmarine: name === 'ringo',
})),
) // OK
name만으로 이뤄진 객체로 나타나길 바랬는데 이 코드가 통과를 한다.
const double = (x: number) => 2 * x;
assertType<(a: number, b: number) => number>(double);
인자를 하나만 받으라고 선언했음에도 통과한다.
이는 선언된 것보다 적은 매개변수를 가진 함수를 할당하는 것이 아무런 문제가 없다는 것을 보여준다.
오히려, 매개변수의 개수가 맞지 않는다고 해서 에러를 발생시키면 JS 코드에서 콜백 함수의 타입과 관련된 오류들이 발생할 것이다.
DefinitelyTyped의 타입 선언을 위한 도구는 dtslint이다.
dtslint는 할당 가능성을 체크하는 대신 각 심벌 타입을 추출해 글자 자체가 같은지 비교한다.
더 엄격한 테스트를 위해 dslint같은 도구를 활용하는 것도 좋다.
'프로그래밍언어 > TypeScript' 카테고리의 다른 글
이펙티브 타입스크립트 스터디 - 7주차 발표 정리 (0) | 2024.06.22 |
---|---|
이펙티브 타입스크립트 스터디 - 5주차 발표 정리 (1) | 2024.06.07 |
이펙티브 타입스크립트 스터디 - 4주차 발표 정리 (0) | 2024.06.01 |
이펙티브 타입스크립트 스터디 - 3주차 발표 정리 (0) | 2024.05.25 |
이펙티브 타입스크립트 스터디 - 1주차 발표 정리 (0) | 2024.05.11 |