TypeScript, Understanding the code you write
Tags: typescript, javascript, webdev, frontend
Published: 2024-05-23 17:02:24


TypeScript has given the frontend a powerful chance to deliver reliable and maintainable code. The more you correctly type your code, the more you fall into its strengths and importance. In this post, I'll mention some TypeScript tips that you may or may not know about.

## How TypeScript works?

The first thing you should know is **how TypeScript works**?

<figure style={{ textAlign: "center" }}>
  
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cfpjlgfnfd9l2gjjyx1q.png)


  <figcaption>
    [Source](https://gist.github.com/zeyadetman/e2435ca1e2e0fe78d3dc981250e42499)
  </figcaption>
</figure>

Browsers are designed to only understand JavaScript, just as computers are designed to only understand zeros and ones. In the case of computers, we compile high-level code to machine code and then to zeros and ones so the computer can understand it. Similarly, we transpile or translate TypeScript code to JavaScript code to be understood by browsers and other engines that only know JavaScript.

To do this, we may use the popular `tsc` tool, You can also check out the [awesome-typescript-compilers](https://github.com/JohnDeved/awesome-typescript-compilers?tab=readme-ov-file) repository to learn more about them.

For example, code like this:

```ts
type animals = "cat" | "dog";
const getAnimal = (animal: animals) => animal;
getAnimal("cat");
```

Is translated to this JavaScript code, so browsers can understand it:

```js
const getAnimal = (animal) => animal;
getAnimal("cat");
```

**Note: TypeScript code is eliminated in the transpiling process.**

This leads us to an **important conclusion**: once you realize it, you'll notice a lot about TypeScript concepts and why they've been written in their particular way.

**Conclusion: As TypeScript does not run on browsers, it only runs at compile time, not at runtime.**

> **Compile time** is the time when your code is being built, before it runs and becomes interactive. **Run time** is when your code is running and interacting with the client.

### Differences in Action

Let's compare a typed language that checks types at runtime, like C#, with TypeScript, which checks types at compile time:

#### Function overloading

> Some JavaScript functions can be called in a variety of argument counts and types. For example, you might write a function to produce a `Date` that takes either a timestamp (one argument) or a month/day/year specification (three arguments).
> [Resource](https://www.typescriptlang.org/docs/handbook/2/functions.html#function-overloads)

In C#, we can achieve function overloading by declaring multiple function arguments and bodies. This is possible because C# checks the type of the function arguments at runtime and defines the function body accordingly:

```c#
void display() { ... }
void display(int a) { ... }
float display(double a) { ... }
float display(int a, float b) { ... }
```

However, in TypeScript, we go to the runtime without types, without typescript itself, so there's no runtime type checks and then we can't assign multiple values to the same variable in javascript, or in other words we can't create multiple bodies for the same function.

```ts
function makeDate(timestamp: number): Date;
function makeDate(m: number, d: number, y: number): Date;
function makeDate(m: number, d: string, y: number): string;
function makeDate(
  mOrTimestamp: number,
  d?: number | string,
  y?: number
): Date | string {
  if (typeof d === "string") {
    return "hello";
  }

  if (d !== undefined && y !== undefined) {
    return new Date(y, mOrTimestamp, d);
  } else {
    return new Date(mOrTimestamp);
  }
}

const d1 = makeDate(1234);
const d2 = makeDate(5, 5, 5);
```

This will be compiled to this Javascript code:

```js
function makeDate(mOrTimestamp, d, y) {
  if (typeof d === "string") {
    return "hello";
  }
  if (d !== undefined && y !== undefined) {
    return new Date(y, mOrTimestamp, d);
  } else {
    return new Date(mOrTimestamp);
  }
}

const d1 = makeDate(1234);
const d2 = makeDate(5, 5, 5);
```

There's libraries can do runtime checks for javascript, one of the most popular libraries do this is [zod](https://zod.dev/) you can give it a try.

## Narrowing (make it more specific)

## `keyof` and `typeof`

- `keyof` from its name, used to "extract" a key of a TYPE, and since objects are the only datatype that contains keys, then `keyof` is used to extract key of an object in a **union** format

```ts
type obj = { [n: number]: string };
type A = keyof obj; // number
```

- `typeof` from its name, used to return the `type` of a variable. simple!

```ts
let str = "Alia";
type a = typeof str; // string

const str2 = "Alia";
type b = typeof str2; // "Alia"
```

**Important note: I used `let` here instead of `const`. If I used `const`, then the type would be the specific string value itself.**

Let's merge both

```ts
const obj = {
  id: 1,
  name: "Alia",
}; // javascript variable no types

type typeOfObj = typeof obj; // type of the javascript object
type keysOfObj = keyof typeOfObj; // id | name

type keysOfObjOneLine = keyof typeof obj;
```

Notice here

- `keyof` extracts keys of type (object).
- `typeof` extracts the type of variable.

_keep this in your mind for the mapped types section._

## `in` operator and `extends`

Similar to the previous section, `in` operator is kinda similar way you can use it to limit the key to be within a group of types

```ts
type keysOfObj = "id" | "name";
type obj = { [key in keysOfObj]: string };

// type obj = {
//  id: string;
//  name: string;
// }
```

But we need to modify the code to fix the type for the `id` key to be `number` not `string`

- `extends` consider it as `if else` of typescript.

```ts
type keysOfObj = "id" | "name";
type obj = { [key in keysOfObj]: key extends "id" ? number : string };

// That's it
```

_This called mapped types_

## Generics

Think about it as a function in javascript, if you have a type and you're repeating it with slight changes, then you probably need to use `generics`.

```ts
interface BasicType<T> {
  input: T;
}

const x: BasicType<string> = { input: "test" };
const y: BasicType<number> = { input: "test" }; // wrong
```

```ts
function basic(a, b) {
  return { a, b };
}

function basic<A, B>(a, b) {
  return { a, b };
}

function basic<A, B>(a: A, b: B): { a: A; b: B } {
  return { a, b };
}

basic<string, number>("hello", 2);
```

we can still use `extends` with generics to give a powerful type.

## Typescript Coverage

In addition to adding TypeScript rules to your project, it's important to track TypeScript code coverage. Code coverage helps identify missing types for variables.

You might think it's obvious if a variable has a type, but when using third-party libraries, classes, or functions, it's easy to miss some. Not everything, such as query parameters, will be tracked by the TypeScript coverage library, but it definitely helps.

I suggest using [typescript-coverage-report](https://www.npmjs.com/package/typescript-coverage-report) for this purpose. If you know of a better option, feel free to share it in the comments below.

Resources:

- [TypeScript Documentation](https://www.typescriptlang.org/docs/)
- [type-challenges](https://github.com/type-challenges/type-challenges)
- [typehero.dev](https://typehero.dev/)

[Find more... ](https://notes.zeyadetman.com/programming-languages/typescript)

---

_While writing this post, civilians and children in Gaza are being killed in the ongoing genocide. It's important to be informed about what is happening in Palestine and to consider the humanitarian aspects of the situation. Please take the time to research, and understand the events and their impact on palestinian's lives. [know more](https://blog.paulbiggar.com/i-cant-sleep/)_


_Originally Published on [https://zeyadetman.com/posts/typescript-understand-the-code-you-write](https://zeyadetman.com/posts/typescript-understand-the-code-you-write)_