Typescript Tip 01: Using the “in” Keyword to Deal with Multiple Types

Muggle-born
3 min readApr 14, 2022

--

Today we’re talking about Type Narrowingspecial checks and assignments that allow typescript to infer more specific types than declared — and how you can use the in keyword to distinguish between several types.

What is Type Narrowing?

Imagine we have a simple function called padLeft which adds padding to the left of the given input. There are two cases our padLeft function needs to handle

  1. If padding is a number, it will treat that as the number of spaces we want to prepend to input.
  2. If padding is a string, it should just prepend padding to input.

Below is an implementation of padLeft, which illustrates type narrowing.

function padLeft(padding: number | string, input: string) {
if (typeof padding === "number") {
return " ".repeat(padding) + input;
}
return padding + input;
}

It may not seem like much, but TypeScript is doing something pretty clever here. Within our if check, TypeScript sees typeof padding === “number” and understands that as a particular form of code called a type guard. TypeScript uses the type guard to infer more specific types for the padding variable.

In the example above, TypeScript used type narrowing and inferred:

  • (parameter) padding: number on line 3
  • (parameter) padding: string on line 5

The Problem We Are Trying to Solve

Take a look at the code below, there are two typescript errors ❌ because TypeScript is not able to narrow types between A and B. We’ll see how we can update the code in the section below and resolve these typescript errors.

interface A {
x: string
doSomething: () => boolean;
}
interface B {
x: string
doAnotherThing: () => boolean;
}
// FAIL
function example(value: A | B) {
// ❌ Property 'doSomething' does not exist on type 'A | B'
if (value.doSomething && value.doSomething()) {
//....
}
// ❌ Property 'doAnotherThing' does not exist on type 'A | B'
if (value.doAnotherThing && value.doAnotherThing()) {
//...
}
}

Using the “in" keyword

JavaScript has an operator for determining if an object has a property with a name: the in operator. TypeScript takes this into account as a way to narrow down potential types. Let’s revisit the example above and update our code to use the in operator.

// PASS
function example(value: A | B) {
if ('doSomething' in value && value.doSomething()) { // ✅ CODE UPDATED
//...
}
if ('doAnotherThing' in value && value.doAnotherThing()) { // ✅ CODE UPDATED
//...
}
}

Similar to the typeof operator we saw being used in the first section, the conditional check 'doSomething' in value narrows the type of value to A, and the conditional check 'doAnotherThing' in value narrows the type of value to B.

👋 That’s all for this week; try solving the problems below if you fancy a challenge. Otherwise, come back next week for another tip!

Challenge for the Reader

Remove all the TypeScript errors in my custom TS Playground to test your knowledge of Type Narrowing.

Additional Resources

We only covered some of the many ways you can narrow types in TypeScript. Check out https://www.typescriptlang.org/docs/handbook/2/narrowing.html if you’re interested in learning more about them.

--

--

Muggle-born
Muggle-born

Written by Muggle-born

Hi, my name is Jeremiah. I build things on the web for fun. I enjoy figuring how and why things work the way they do, and I try teaching others along the way.

Responses (1)