TSworkingts-evergreen

User-defined type guards

Teach the compiler what you already know.

A function returning arg is T (or asserts arg is T) hands the compiler proof it cannot derive on its own β€” so narrowing survives across calls, filters, and runtime validation.

The is predicate

A normal boolean function tells the compiler nothing about why it returned true. Give the return type the form x is Foo and a true result narrows x to Foo in the calling branch. The body still returns a plain boolean β€” the predicate is purely a signal to the type system, so it is your job to make the runtime check actually match the claim.

Filtering unions

Array.prototype.filter normally keeps the element type unchanged β€” filtering a (string | undefined)[] still yields (string | undefined)[]. Pass a predicate typed as x is string and the compiler narrows the result array to string[]. This is the idiomatic way to drop null/undefined without a cast.

Assertion functions

A function typed asserts x is T narrows by throwing instead of returning. After the call returns normally, x is T for the rest of the scope β€” no if needed. The bare form asserts x narrows away null/undefined/false. These power runtime validators where an invalid value should abort, not branch.

Soundness is on you

The compiler trusts your predicate blindly. If isString returns true for a number, every downstream narrowing is a lie and you get runtime errors with no compile warning. Keep the runtime check and the asserted type in lockstep.

Try it 5 examples

A basic isString predicate

TSintro
Press Run to execute

Only 'hello' enters the narrowed branch and prints HELLO; the 42 and null calls both fall through to (not a string). The predicate, not a cast, is what made value.toUpperCase() type-safe inside the if.