DEV Community

Zeyad Etman
Zeyad Etman

Posted on

TypeScript, Understanding the code you write

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?

Image description


Source

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 repository to learn more about them.

For example, code like this:

type animals = "cat" | "dog";
const getAnimal = (animal: animals) => animal;
getAnimal("cat");
Enter fullscreen mode Exit fullscreen mode

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

const getAnimal = (animal) => animal;
getAnimal("cat");
Enter fullscreen mode Exit fullscreen mode

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

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:

void display() { ... }
void display(int a) { ... }
float display(double a) { ... }
float display(int a, float b) { ... }
Enter fullscreen mode Exit fullscreen mode

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.

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);
Enter fullscreen mode Exit fullscreen mode

This will be compiled to this Javascript code:

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);
Enter fullscreen mode Exit fullscreen mode

There's libraries can do runtime checks for javascript, one of the most popular libraries do this is zod 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
type obj = { [n: number]: string };
type A = keyof obj; // number
Enter fullscreen mode Exit fullscreen mode
  • typeof from its name, used to return the type of a variable. simple!
let str = "Alia";
type a = typeof str; // string

const str2 = "Alia";
type b = typeof str2; // "Alia"
Enter fullscreen mode Exit fullscreen mode

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

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;
Enter fullscreen mode Exit fullscreen mode

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

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

// type obj = {
//  id: string;
//  name: string;
// }
Enter fullscreen mode Exit fullscreen mode

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.
type keysOfObj = "id" | "name";
type obj = { [key in keysOfObj]: key extends "id" ? number : string };

// That's it
Enter fullscreen mode Exit fullscreen mode

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.

interface BasicType<T> {
  input: T;
}

const x: BasicType<string> = { input: "test" };
const y: BasicType<number> = { input: "test" }; // wrong
Enter fullscreen mode Exit fullscreen mode
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);
Enter fullscreen mode Exit fullscreen mode

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 for this purpose. If you know of a better option, feel free to share it in the comments below.

Resources:

Find more...


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

Originally Published on https://zeyadetman.com/posts/typescript-understand-the-code-you-write

Top comments (0)