DEV Community

Cover image for Type Constraints In TypeScript
Gio
Gio

Posted on

3 2

Type Constraints In TypeScript

Photo credit: https://unsplash.com/photos/ar2dUoleWYA


Suppose you're a back end API developer and you need a way to guarantee at compile time that you're only sending json-serializeable data out of your API.

You have a send function that takes some data and sends it to a API consumer.

const send = <T>(data: T): Promise<null> => {
  // magic...
  // or more realistically
  // calls express' res.json() method!
}
Enter fullscreen mode Exit fullscreen mode

And you're trying to prevent a dev from trying to send the following out of your api:

send({
  makesNoSense: () => { console.log('woops!') },
  myDate: new Date(), 
})
Enter fullscreen mode Exit fullscreen mode

The above would be stringified (i.e. serialized) under the hood into { myDate: 'iso-date-string' }. Functions aren't part of the JSON spec and thus would be removed entirely. And Dates are automatically cast to strings which is not a very efficient way of sending timestamps down the network (hint: you want unix timestamps in the form of an integer).

Woops! Looks like a dev forgot to call a function and also forgot to call Date.getTime 😭

So how do we prevent this sort of thing?

Type Constraints To The Rescue

A type constraint is a "rule" that narrows down the possibilities of what a generic type could be.

For example, in the the send definition above, we declared a type variable T that is not constrained at all. Which is why we were able to call send with values that aren't JSON serializeable.

// This compiles ... API would send `{}`
send(new Set([1,2,3]))
Enter fullscreen mode Exit fullscreen mode

We can thus narrow the possibilities of the generic type T to allow allow JSON values as follow:

const send = <T extends JSONValues>(data: T): Promise<null> => {
  // ...
}
Enter fullscreen mode Exit fullscreen mode

The only difference is that now we've appended extends JSONValues to the type variable declaration for T.

In plain english T extends JSONValues translates to, "T is a generic type that must conform to the definition of JSONValues".

What's JSONValues?

It's defined as this:

type JSONValues
    = number
    | string
    | null
    | boolean
    | { [k: string ]: JSONValues }
    | JSONValues[]
Enter fullscreen mode Exit fullscreen mode

... Yup, this is the entire JSON specification in 7 lines of code! 🤯

Now, if I call send(new Set([1,2,3])) I will get a type error. Whaaaat?!?!

Now you can guarantee at compile-time that you will only send valid data to your JSON API consumers :)

Live demo

Conclusion

Type constraints are a very powerful way to supercharge your typescript codebases.

For each generic type variable that you'd like to constrain, you would append the extends SomeTypeName to the definition. Example:

const myFn = <T extends JsonValues, U extends FinancialData>() => {...}
Enter fullscreen mode Exit fullscreen mode

Hope that helps!


Shameless Plug

Liked this post?

I stream functional programming, TypeScript and Elm development every Tuesday at 10am on Twitch!

https://www.twitch.tv/vimboycolor

AWS GenAI LIVE image

Real challenges. Real solutions. Real talk.

From technical discussions to philosophical debates, AWS and AWS Partners examine the impact and evolution of gen AI.

Learn more

Top comments (0)

Qodo Takeover

Introducing Qodo Gen 1.0: Transform Your Workflow with Agentic AI

Rather than just generating snippets, our agents understand your entire project context, can make decisions, use tools, and carry out tasks autonomously.

Read full post

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay