// lib.es5.d.ts
interface Array<T> {
filter<S extends T>(predicate: (value: T, index: number, array: T[]) => value is S, thisArg?: any): S[];
filter(predicate: (value: T, index: number, array: T[]) => unknown, thisArg?: any): T[];
}
const filtered = [1,2,3,4,5].filter(value => value % 2)
filter는 두가지 타입이 정의되어 있는데, 첫번째 타입을 기준으로 분석해보자
filtered함수에 Array<T>
에 Array<number>
로 들어가 T
는 number
가 되고, S extendes number
로 함수의 리턴값에 S[] -> number[]
가 추론된다
반면에 문자열과 숫자가 섞인 로직을 분석해보자
const result = [1,'2',3,'4',5].filter((value) => typeof value === 'string');
여기서 result
의 타입은 무엇이 나올까?
string[]
으로 나올 것 같지만, (string | number)[]
가 나온다. 왜일까?
그 이유는 filter 타입에서 확인할 수 있다
T
에 (string | number)[]
값이 들어가고, S extends T -> S extends (string | number)[]
로 추론하기 때문에, 함수의 리턴값(결과값)이 (string | number)[]
로 나올 수 밖에 없다
그럼 더 정확한 타입인 string[]
으로 추론하고 싶다면 어떻게 하면 될까?
filter의 정의된 타입을 보면 value is S
를 찾을 수 있다
/**
* Step1 - 콜백함수를 새로 선언
*/
const cb = (value) => typeof value === 'string'
const result = [1,'2',3,'4',5].filter(cb);
/**
* Step2 - value is string을 통해 타입가드 적용
*/
const cb = (value): value is string => typeof value === 'string'
const result = [1,'2',3,'4',5].filter(cb);
value is string
를 사용하여 Type Guard(타입가드)
로 변수 result
타입을 string[]
으로 한정된 범위로 추론할 수 있다