타입가드로 알고있었는데 공식문서에서도 type precate 라고한다.
type precate
의 동작
type typePredicate = (type: Type) => type is NarrowType;
저런 타입함수가 실행되었을때 Type 의 종류에 따라 동작이 다르다.
getNarrowedTypeWorker
function getNarrowedTypeWorker(...){
...
const narrowedType = mapType(candidate, c => {
// If a discriminant property is available, use that to reduce the type.
const discriminant = keyPropertyName && getTypeOfPropertyOfType(c, keyPropertyName);
const matching = discriminant && getConstituentTypeForKeyType(type as UnionType, discriminant);
// For each constituent t in the current type, if t and and c are directly related, pick the most
// specific of the two. When t and c are related in both directions, we prefer c for type predicates
// because that is the asserted type, but t for `instanceof` because generics aren't reflected in
// prototype object types.
const directlyRelated = mapType(
matching || type,
checkDerived ?
t => isTypeDerivedFrom(t, c) ? t : isTypeDerivedFrom(c, t) ? c : neverType :
t => isTypeStrictSubtypeOf(t, c) ? t : isTypeStrictSubtypeOf(c, t) ? c : isTypeSubtypeOf(t, c) ? t : isTypeSubtypeOf(c, t) ? c : neverType,
);
...
// 1
return !(narrowedType.flags & TypeFlags.Never) ? narrowedType :
isTypeSubtypeOf(candidate, type) ? candidate :
isTypeAssignableTo(type, candidate) ? type :
isTypeAssignableTo(candidate, type) ? candidate :
getIntersectionType([type, candidate]);
}
mapType
function mapType(type: Type, mapper: (t: Type) => Type | undefined, noReductions?: boolean): Type | undefined {
if (type.flags & TypeFlags.Never) {
return type;
}
// label: not union
if (!(type.flags & TypeFlags.Union)) {
return mapper(type);
}
const origin = (type as UnionType).origin;
const types = origin && origin.flags & TypeFlags.Union ? (origin as UnionType).types : (type as UnionType).types;
let mappedTypes: Type[] | undefined;
let changed = false;
for (const t of types) {
// 1
const mapped = t.flags & TypeFlags.Union ? mapType(t, mapper, noReductions) : mapper(t);
changed ||= t !== mapped;
if (mapped) {
if (!mappedTypes) {
mappedTypes = [mapped];
}
else {
mappedTypes.push(mapped);
}
}
}
// 2
return changed ? mappedTypes && getUnionType(mappedTypes, noReductions ? UnionReduction.None : UnionReduction.Literal) : type;
}
mapped type 의 mapper
로 t => isTypeStrictSubtypeOf(t, c)...
이 들어가고
1
에서는 union 각 개별타입이 c
즉 this is c
의 subset 인지 검사하고 t,c 중에서 subset 을 mappedTypes
에 push
2
return changed ? mappedType 마지막 return 문에서 subset 인것들만 union 타입으로 만들어 return 한다.
type A = { kind: 'A'; data: 1 };
type B = { kind: 'B'; data: 2 };
type C = { kind: 'C'; data: 3 };
type ABC = A | B | C;
declare const isA: (value: unknown) => value is A;
declare const abc: ABC;
if (isA(abc)) {
abc
// ^? const abc: A
}
not union
에서 subset 을 검사한다.
등 많은 함수를 거쳐서 검사한다.