타입가드로 알고있었는데 공식문서에서도 type precate 라고한다.

type precate

Using type predicates

의 동작

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;
}

type 이 union 일때

mapped type 의 mappert => isTypeStrictSubtypeOf(t, c)... 이 들어가고

1 에서는 union 각 개별타입이 cthis 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
}

type 이 union 이 아닐때

not union 에서 subset 을 검사한다.

등 많은 함수를 거쳐서 검사한다.