I've been writing TypeScript without understanding it
Tags: typescript, javascript, tutorial, learning
Published: 2024-06-11 12:00:00

## I admit, I don’t really understand TypeScript

The other day, I was stuck with a bug in some code that was handling optimistic updates, so I asked my colleague [Filip](https://tenor.com/en-GB/view/the-office-funny-kevin-malone-phillip-i-hate-phillip-gif-16434707) for some help. Filip, a TypeScript wizard, mentioned that the `satisfies` keyword would be part of the solution I was looking for.

*`Satisfies`? What the heck is that? And why had I never heard of it before?* I mean, I’ve been using TypeScript for some time now, so I was surprised I didn’t know it myself.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/v6kny7wtjnc4168yn2o0.png)


Not too long after that, I stumbled across this tweet from @yacineMTB, prolific yapper and engineer at [X.com](http://X.com) (aka Twitter):

> like, why can't i just *run* a **typescript** file? what's the point of a scripting language if i need to init a whole directory and project along with it?
> 

Again, I found myself wondering why I didn’t already know that about TypeScript. *Why couldn’t you actually run a TypeScript file? What was the difference between a scripting language and a compiled language?*

[![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dy01hirmxhlwybk6mx9u.png)](https://opensaas.sh)

It hit me that I didn’t quite understand some fundamental things about the language I was using nearly every day to create things like [Open SaaS](https://opensaas.sh), a free, open-source SaaS starter.

So I decided to take a step back, and did some investigating into these topics. And in this article, I’m going to share with you some of the most important things I learned.

## What Type of Script is TypeScript?

You’ve probably already heard that TypeScript is a “superset” of JavaScript. This means that it’s an added layer on top of JavaScript, in this case, that lets you add static typing to JavaScript. 

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/z5595bx8k88hkih2pthh.png)

it’s kind of like TypeScript is the Premium version of JavaScript. Or, put another way, if JavaScript were a base model Tesla Model 3, TypeScript Would be the Model X Plaid. *Vroooom.*

But because it is a *superset* of JavaScript, it doesn’t really run the way JavaScript itself does. For example, JavaScript is a scripting language, which means the code gets interpreted line-by-line during execution. It was designed this way to be run in web browsers across different operating systems and hardware configurations. This differs from lower-level languages like C, which need to get compiled into machine code first for specific systems before it can be executed.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/iu22drd35u217gatsifn.png)

So, JavaScript doesn’t have to be compiled first but gets interpreted by the JavaScript engine. TypeScript, on the other hand, has to get converted (or ”transcompiled”) into JavaScript before it can be executed by a JavaScript engine in the browser (or as a standalone NodeJS app).

So the process looks a bit like this:
```
 → Write TypeScript Code

    → “Transcompile” to JavaScript

      → Interpret JavaScript & Check for Errors

        → JavaScript Engine Compiles and Executes the Code
```

Pretty interesting, right?

But now that we’ve got some of the theoretical stuff out of the way, let’s move on to some more practical things, like the thing TypeScript is known for: it’s *Types!*

---

By the way…

We're working hard at [Wasp](https://wasp-lang.dev/) to create the best open-source React/NodeJS framework that allows you to move fast!

That's why we've got ready-to-use full-stack app templates, like a ToDo App with TypeScript. All you have to do is install Wasp:

```sh
curl -sSL https://get.wasp-lang.dev/installer.sh | sh
```

and run:

```sh
wasp new -t todo-ts
```

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jj26gymrxyybut4541lg.png)

You'll get a full-stack ToDo app with Auth and end-to-end TypeSafety, out of the box, to help you learn TypeScript, or just get started building something quickly and safely :)

---

## Playing Around with `satisfies`

Remember how I asked my colleague for help, and his solution involved the `satisfies` keyword? Well, to understand it better I decided to open an editor and play around with some basic examples, and this is what I found the be the most useful thing I learned.

To start, let’s take the example of a person object, and let’s type it as a `Record` that can take a set of `PossibleKeys` and a `string` or `number` as the values. That would like look this:

```ts
type PossibleKeys = "id" | "name" | "email" | "age";

const person: Record<PossibleKeys, string | number> = { } 
```

The way we typed the `person` constant is called a Type Annotation. It comes directly after the variable name.

Let’s start adding keys and values to this `person` object:

```ts
type PossibleKeys = "id" | "name" | "email" | "age";

const person: Record<PossibleKeys, string | number> = {
  id: 12,
  name: "Vinny",
  email: "vince@wasp-lang.dev",
  age: 37,
} 
```

Looks pretty straightforward, right? 

Now, Let’s see how TypeScript inferred the types of the `person` properties:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8bd9m3bfy19qep0mvz98.png)

Interesting. When we hover over `email`, we see that TypeScript is telling us that email is a union type of either a `string` OR a `number` , even though we definitely only defined it as a `string`.

This will have some unintended consequences if we try to use some `string` methods on this type. Let’s try the `split` method, for example:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6u4nxa0em6mr0qqghuyp.png)

We’re getting an error that this method doesn’t work on type `number`. Which is correct. But this is annoying because we know that `email` is a string.

Let’s fix this with `satisfies` by moving the type down to the end of the constant definition:

```ts
type PossibleKeys = "id" | "name" | "email" | "age";

const person = {
  id: 12,
  name: "Vinny",
  email: "vince@wasp-lang.dev",
  age: 37,
} satisfies Record<PossibleKeys, string | number>;
```

Now, when hover over the `email` property, we will see it is correctly inferred as a `string` :

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lxv578os1altgpkgmojo.png)

Nice! Now we won’t have any issues using `split` to turn the `email` into an array of strings.

And this is where `satisfies` really shines. It let's us validate that the Type of an expression matches a certain Type, while inferring the narrowest possible Types for us.

## Excess Property Checking

But something else strange I noticed when I was playing with `satisfies` was that it behaved differently if I used it directly on a variable versus on an intermediate variable, like this:

```ts
// Directly on object literal
const person = { } satisfies PersonType;

// Using on intermediate variable
const personIntermediate = person satisfies PersonType
```

Specifically, if I add another property to the `person` object that doesn’t exist in the type, like `isAdmin`, we will get an error when with the direct use, but we won’t with the intermediate variable:

1. Directly using `satisfies` 

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dzqk2ph21jkcbdu4w6oz.png)

2. Using `satisfies` with an intermediate variable

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/haipxyc4ihxpmezydgto.png)

You can see that in example 2, there is no error and person “satisfies” the `PersonType`, although in example 1 it does not.

Why is that? 

Well, this actually has more to do with how JavaScript fundamentally works, and less to do with the `satisfies` keyword. Let’s take a look.

The process occurring in the examples above is what’s referred to as “Excess Property Checking”.

Excess property checking is actually the exception to the rule. TypeScript uses what’s called a “Structural Type System”. This is just a fancy way to say that **if a value has all the expected properties, it will be used.** 

So using the `personIntermediate` example above, TypeScript didn’t complain that `person` had an extra property, `isAdmin`, that didn’t exist in the `PersonType`. It had all the other necessary  properties, like `id`, `name`, `email`, and `age`, so TypeScript accepts it in this intermediate form.

But when we declare a type directly on a variable, as we did in example 1, we get the TypeScript error: “’isAdmin’ does not exist in type ‘PersonType’”. **This is Excess Property Checking at work** and it’s there to help you from making silly errors.

It’s good to keep this in mind, as this will help you to avoid unintended side-effects. 

For example, let’s say we change the person type to have an optional `isAdmin` propert, like this:

```ts
type PersonType = {
  id: number,
  name: string,
  isAdmin?: boolean, // 👈 Optional
}
```

What would happen if we accidentally defined `person` with an `isadmin` property instead of `isAdmin` and didn’t declare the type directly? 

We would get no error from TypeScript because `person` actually does satisfy all the necessary types. The `isAdmin` type is optional, and it doesn’t exist on `person` , but that doesn’t matter. And you’ve made a simple type-o and now are trying to access the `isAdmin` property and it doesn’t work:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jxz8zzlxxog0xkdfpvbn.png)

Whoops! Let’s fix it with a type annotation, where we declare the type right away:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zer5v40ipp47juw2n50f.png)

Nice. Because we used a direct type annotation on line 58, we get the benefits of TypeScript’s excess property checking.

Thanks, TypeScript! 🙏

---

If you found this content useful, and want to see more like it, you can help us out really easily by [giving Wasp a star on GitHub!](https://www.github.com/wasp-lang/wasp).

![https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qgbmn45pia04bxt6zf83.gif](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qgbmn45pia04bxt6zf83.gif)

{% cta [https://www.github.com/wasp-lang/wasp](https://www.github.com/wasp-lang/wasp) %} ⭐️ Star Wasp on GitHub 🙏  {% endcta %}

---

## To Be Continued…

Thanks for joining me on part 1 of my journey into better understanding the tools we use everyday. 

This will be an ongoing series where I will continue to share what I learn in a more exploratory, and less structured, way. I hope you found some part of it useful or interesting.

Let me know what you’d like to see next! Did you enjoy this style? Would you change something about it? Add or remove something? Or do you have an opinion or similar story about something you’ve learned recently?

If so, let us know in the comments, and see you next time :)