Why must a type predicate's type be assignable to its parameter's type?

A user-defined type guard performs a runtime check to determine whether or not value of a particular type satisfies a type predicate.

If there is no relationship between the value's type and the type in the type predicate, the guard would make no sense. For example, TypeScript won't allow a user-defined guard like this:

function isString(value: Date): value is string {
    return typeof value === "string";
}

and will effect this error:

[ts] A type predicate's type must be assignable to its parameter's type.
Type 'string' is not assignable to type 'Date'.

A Date value will never be a string, so the guard is pointless: its runtime check is unnecessary and should always return false.

When you specify a generic, user-defined type guard, T could be anything, so - as with Date - for some types, the type guard would make no sense.

If you really don't want to use any, you could use an empty interface - {} - instead:

function isString(value: {}): value is string {
    return typeof value === "string";
}

If you also want to allow for null and undefined values to be passed to the guard, you could use:

function isString(value: {} | null | undefined): value is string {
    return typeof value === "string";
}

Regarding the error message, the predicate type must be assignable to the value type because the type guard is used to check whether a value with a less-specific type is in fact a value with a more-specific type. For example, consider this guard:

function isApe(value: Animal): value is Ape {
    return /* ... */
}

Ape is assignable to Animal, but not vice versa.

Tags:

Typescript