Type checking helps productivity by preventing careless mistakes. Type checking also makes code more self documenting.

TypeScript is ultimately just JavaScriptâ„¢, though. There are situations in JavaScript (and by association, TypeScript) that are inherently dynamic and can’t be statically analyzed until runtime.

Some examples of dynamic code that can’t easily be type checked include:

  • 3rd party libraries that use eval (we wish they wouldn’t).
  • Incoming data that must be parsed at runtime, such as HTTP form data.
  • Handling unstructured or schema-less data sources.
  • Dealing with data streams from messaging buses.

For the cases above, it is impossible to know data types for sure until runtime. It would seem that TypeScript can’t help in this situation and we are stuck using the any keyword. Although some situations warrant the use of an explicit any, I prefer to avoid it when possible.

Setting the Stage

Enter the user defined type guard. Added in TypeScript 1.6, they provide a way of relaying runtime type information to the compiler.

User defined type guards are boolean functions that perform type checking at runtime. Here’s a trimmed-down example:


// A "User" interface, for the sake of example.
interface User { id: number; name: string; }

// Notice the syntax below ("x is y").
// This is a "user defined type guard".
function isUser(usr: any): usr is User {
  // This user defined function checks
  // the following conditions:
  //  * "usr" is an object
  //  * "usr" is has a "name" property (string)
  //  * "usr" is has an "id" property (number)
  let isObject = () => typeof usr === "object";
  let hasName = () => typeof usr.name === "string";
  let hasId = () => typeof usr.id === "number";

  // Return bool indicating that the conditions were met.
  return (isObject() && hasName() && hasId());
}

From the outside, this might look like an ordinary function declaration. The important difference is the usr is User syntax. It tells the compiler that the function is able to determine the type of usr at runtime.

Let’s take a look at why this is versatile for dynamic programming scenarios.

Adding Safety

Scenario: Developers are posting API data to our application via HTTP. Sometimes, the data is improperly formatted. Typescript can handle this situation easily thanks to the previously defined type guard.


// Developers are `POST`ing data to a `/users`
// endpoint. We are unsure if the data they are
// posting is correctly formatted.
let suspiciousData = JSON.parse(request.body);

// Same `isUser` function from previous section...
if (isUser(suspiciousData)) {
  // COMPILER IS CERTAIN OF TYPE IN THIS BRANCH.
  // Enough information is available to infer the type.
  // Send the user confirmation.
  var msg = "RECEIVED DATA: " + suspiciousData.id;
  response.send(msg, 201);
} else {
  // Typescript evades disaster yet again!
  // Will be an "any" type here.
  response.send("BAD DATA", 422);
};

So why is this better than a simple boolean function or conditional logic? It’s important to keep in mind that user defined type guards help the compiler do its job.

If you’re using a TypeScript IDE, you will now see that when you mouse over this variable, TypeScript knows what’s going on! Conversely, if you tried to call nonexistent User attributes (perhaps you misspelled .email), the type checker would catch these errors. The same can not be said of simple boolean type checking functions.

Some Slight Issues

There is a known bug as of this writing that prevents user defined type guards from refining an any type.

One fix for the time being is to apply a union type of x|Object, where x is the expected type of your type guard. Hopefully this issue is resolved in future versions of typescript.

I’ve created an example of this issue on the TypeScript playground to illustrate.

fig1

Despite this temporary issue, it is still a useful feature, as it offloads the burden of type checking to the compiler.

Other Resources

For a deeper look, check out the Advanced Types section of the TypeScript Handbook.

Additionally, if you find yourself writing type guards with higher levels of complexity, consider pairing the type guard with a validation library like Joi or Validate.js.

Have questions? Say “hi” in the comments below.

Enjoyed the article? Consider signing up for our ever-so-occasional email newsletter.

comments powered by Disqus