Matt Pocock Profile picture
Mar 2 18 tweets 6 min read
satisfies in TypeScript has been out for a little while now.

Overall, it's been a success - but it's starting to cause some confusion.

Let's clear it up. const obj = {} satisfies Record<string, string>;  // Propert
satisfies has added yet another tool to the TypeScript user's toolkit.

There are now _three_ ways to assign types to values.

First, there's the humble 'colon annotation'.

This concept isn't really given a name in the TS docs, so I'll use this slightly medical name.

This lets you say 'this variable is always this type'. const obj: Record<string, string> = {};  obj.id = "123&
When you use a colon annotation, you're _declaring_ that the variable is that type.

That means that the thing you assign to the variable _must_ be that type. // Type 'number' is not assignable to type 'string'. const s
This means you can actually give a variable a type that's _wider_ than the one you initially assign.

This is useful when you want to have a default which might later be reassigned. let id: string | number = "123";  if (typeof numer
But colon annotations come with an edge-case downside.

When you use one, the type BEATS the value. That means that if you declare a wider type than you want, you're stuck with the wider type.

In this example, you don't get autocomplete on the routes object. const routes: Record<string, {}> = {   "/": {},
This is the problem satisfies was designed to solve.

When you use satisfies, the value BEATS the type. This means it infers the narrowest possible type, not the wider type you specify. const routes = {   "/": {},   "/users":
satisfies also protects you from specifying the wrong thing inside your config object.

So, colon annotations and satisfies are equally safe. const routes = {   // Type 'null' is not assignable to type
The third way you can assign types to variables is the 'as' annotation.

Unlike satisfies and colon annotations, 'as' annotations let you lie to TypeScript. type User = {   id: string;   name: {     first: string;
This has some limits - you can add properties to objects, but you can't convert between basic types.

For instance, you can't force TypeScript to convert a string into a number...

...except if you use the monstrous 'as-as'. // Conversion of type 'string' to type 'number' // may be a
Sometimes, 'as' is needed. For instance, when you're converting an object to a known type. type User = {   id: string;   name: string; };  // The user
But if you're using 'as' as your default way to annotate variables, that's almost certainly wrong.

The code below might look safe, but as soon as you add another property to the User type, the defaultUser will be out of date - and won't error. type User = {   id: string;   name: string; };  const defaul
There's actually a secret _fourth_ way to give a type to a variable.

Don't.

TypeScript does an amazing job at inferring types for your variables.

Most of the time, you won't need to type your variables. const routes = {   "/": {},   "/users":
So, we've got FOUR ways of assigning a type to a variable.

- colon annotations
- satisfies
- as annotations
- not annotating and inferring it
The mistake I'm seeing a lot of devs make with the release of 'satisfies' is to use it as their new default.

This is fine for simple cases like this: type User = {   id: string;   name: string; };  const defaul
But most of the time, the times you want to assign a type to a variable are when you _want_ the type to be wider.

For instance, this case. If we used satisfies here, you wouldn't be able to assign numericId to id. // colon annotation  let id: string | number = "123&quo
So - satisfies shouldn't be your default. It's for edge cases. It should be for when:

- You want the EXACT type of the variable, not the WIDER type.
- The type is complex enough that you want to make sure you didn't mess it up
If you dug this thread, my TypeScript course just launched - and it's on sale for another week.

Thanks for reading! ❤️❤️

totaltypescript.com

• • •

Missing some Tweet in this thread? You can try to force a refresh
 

Keep Current with Matt Pocock

Matt Pocock Profile picture

Stay in touch and get notified when new unrolls are available from this author!

Read all threads

This Thread may be Removed Anytime!

PDF

Twitter may remove this content at anytime! Save it as PDF for later use!

Try unrolling a thread yourself!

how to unroll video
  1. Follow @ThreadReaderApp to mention us!

  2. From a Twitter thread mention us with a keyword "unroll"
@threadreaderapp unroll

Practice here first or read more on our help page!

More from @mattpocockuk

Mar 1
Today's the day.

Total TypeScript is ready. Your Total TypeScript adventure starts now
🧙 Hundreds of videos over three modules
🔥 The deepest exploration of Generics on the market
❤️ Interactive editors, transcripts, and thousands of words of accompanying articles
🪄 Perfect if you've used TS for a while, but want to achieve mastery

totaltypescript.com
Total TypeScript is the distillation of everything I've learned as a library developer and team lead.

For the first time, it compiles the secrets of the open source world into a single resource.

Here's how it came about:
Read 10 tweets
Feb 20
TypeScript annoys me sometimes.

So, I decided to do something about it... const arr = [1, 2, 3, undefined];  const result = arr.filter
It's always bugged me that JSON.parse returns 'any'.

It means that, just by using an extremely common JS method, you're introducing a dangerous type into your codebase. // REAL LIFE  const result = JSON.parse("{}");  co
It would be SO much safer if it could return unknown instead. // FANTASY WORLD  const result = JSON.parse("{}");
Read 12 tweets
Feb 19
👀 import "@total-typescript/ts-reset/fetch";  const
👀👀 import "@total-typescript/ts-reset/json-parse";  c
👀👀👀 import "@total-typescript/ts-reset/filter-boolean"
Read 4 tweets
Feb 17
An extremely incomplete TypeScript features type-safety tier list.

🧵
F-tier

as any
as unknown as X
// @ts-ignore
// @ts-expect-error

In application code, these should be avoided at all costs. You pretty much always have a better option.
D-tier

JSON.parse, fetch, localStorage.getItem (anything in lib.dom.d.ts that returns any)

These are unavoidable, but sneakily introduce any's into your app's code.
Read 8 tweets
Feb 17
Just made three pull requests to @calcom to improve their types in small, subtle ways.

Take a look! You might learn something.
github.com/calcom/cal.com…

Here, there were some globals being stored for safekeeping in the global scope.

I added their types using a declare global, meaning they could be safely accessed (and spotted a subtle bug in the process). ImageImage
github.com/calcom/cal.com…

This one was a bit more complex. There was a class that inherited from a HTMLElement, but there were some assumptions being made about whether shadowRoot.host was defined, and what type it was. Image
Read 9 tweets
Feb 15
If you don't know generics, I promise you'll understand them by the end of this thread.

I like a challenge. export const objKeys = <T extends object>(obj: T) => {   ret
What you might think of as generics in TypeScript is actually three separate concepts:

- Passing types to types
- Passing types to functions
- Inferring types from arguments passed to functions // 1. Passing types to types type PartialUser = Partial<{ id
Let's start with passing types to types.

In TypeScript, you can declare a type which represents an object, primitive, function - whatever you want, really. export type User = {   id: string;   name: string; };  expor
Read 24 tweets

Did Thread Reader help you today?

Support us! We are indie developers!


This site is made by just two indie developers on a laptop doing marketing, support and development! Read more about the story.

Become a Premium Member ($3/month or $30/year) and get exclusive features!

Become Premium

Don't want to be a Premium member but still want to support us?

Make a small donation by buying us coffee ($5) or help with server cost ($10)

Donate via Paypal

Or Donate anonymously using crypto!

Ethereum

0xfe58350B80634f60Fa6Dc149a72b4DFbc17D341E copy

Bitcoin

3ATGMxNzCUFzxpMCHL5sWSt4DVtS8UqXpi copy

Thank you for your support!

Follow Us on Twitter!

:(