A Basic TypeScript Insight for JavaScript Devs
Tags: typescript, javascript
Published: 2021-08-03 21:28:28

---
title: A Basic TypeScript Insight for JavaScript Devs
published: true
tags: typescript, javascript
---

Since I started working on [my SaaS product](https://dev.to/kayis/series/10595), I've learned and worked with TypeScript for some months now. My frontend, backend, and even infrastructure code is written in TypeScript, and I quite enjoy sharing interfaces between these parts of my application with a mono repo.

TypeScript is an excellent addition to JavaScript, but some things took me some time to get into my head, one of them being union types.

This all might seem obvious for people used to static typing, but for me, it wasn't evident at first :D

## Type Annotations

One of the essential features of TypeScript is annotating your variables and functions with types that are based on JavaScript types but will be entirely invisible for JavaScript later.

Tools like [ESBuild](https://esbuild.github.io/) will throw away all TypeScript specific syntax and bundle up the remaining JavaScript.

```typescript
const x: string = getText()
```
Will become
```typescript
const x = getText()
```

Now, this is all nice and good, but it gets confusing with all the types that don't have a direct equivalent in JavaScript.

## The `any` Type

The `any` type is classic; it tells TypeScript to close both eyes and let you do what you want. If you understand JavaScript, it can sometimes be easier to write one line with `any` than ten lines correctly typed with TypeScript. 

Often it's nice to start with `any` to get the type checker to shut up, then program the code as you would with JavaScript and later sprinkle actual types on it.

In this example, I access the `someKey` field without checking anything first. It could be that `x` is `undefined` or an `object` or whatever; I don't care and tell TypeScript that I don't care.

```typescript
function f(x: any) {
  return x.someKey
}
```

It's an untyped type that doesn't have any equivalent in JavaScript other than it could be ... well, any type, haha.

This brings us to one of the hard things for me to understand with static typing in general. Later it will be more obvious, but I think it's already the case with `any`.

There are types in TypeScript that map to multiple JavaScript types at runtime, either implicitly with `any`, or explicitly with unions.

It didn't bother me with `any` because it's a particular case of all types, but later it threw me off with union types.

## Union Types

Union types are multiple types at once at runtime, like `any`; the difference is, union types aren't all but only pre-defined specific types. 

```typescript
type StringOrNumber = string | number
```

The `StringOrNumber` type only allows using a variable typed with it only in contexts where a `string` and a `number` can be used. Otherwise, you must manually check that it's one of both before using it in a `string` or `number` context.

While the name and types I have chosen in this example make this obvious, this isn't often the case in an actual codebase.

The type can have any name, and the union can include any type, even generics.

As a JavaScript developer, I was used to the fact that the type was either unknown and I had to check it (the `any` case) or know what was going on, and I probably was working with some class that wraps some functionality. 

This made using unions supplied by frameworks or libraries not easy to understand for me. Sure, one day, I looked at their definition and was baffled by how simple they were, but I was first confused.

But union types are neither. They tell you before runtime that you can use multiple types in one case, but the union type itself doesn't exist at runtime at all. There is no class called `StringOrNumber`; there is `string` or `number`.

If you then couple this feature with another syntax like modules and generics and use a name that isn't as obvious as `StringOrNumber`, things get even more confusing for the mere JavaScript pleb.

```typescript
type Result<T> = T | Promise<T>
```

First, I'm baffled was `T` is; I mean, sure, it makes `Result` generic, but why doesn't it get a speaking name? Then `Result` isn't any more speaking than `T` either. But what are you going to do? Types as general as this one do need general names.

A variable annotated with `Result<string>` can either contain a `string` or a `Promise<string>`, a promise that resolves to a `string`.

There is never a `Result`; it doesn't exist at runtime even if the name `Result` looks like it (more so than `StringOrNumber`). It's not something like a class that wraps a value or a promise for that value; it's gone at runtime. 

If you wanted to check this in JavaScript explicitly, you would have to either know what you're doing and decide how a `T` is different from a `Promise<T>` or wrap it somehow, but this isn't needed in TypeScript. It forces you to think before you write, so you don't have to implement abstractions that have runtime costs.

Sure, you have to check what it is before using it, but you don't have to learn any new class methods or something to use it.

## Conclusion

Look at type definitions, don't get fooled by some name that sounds cryptic, too general, or just like a class you might have implemented in the past. 

And always keep in mind that (at least most of) TypeScript is just JavaScript, and it goes entirely away at runtime.

A type that doesn't exist at runtime doesn't require you to learn more than you already know about JavaScript.