Simple TypeScript tricks for scalable apps
Tags: javascript, typescript, webdev
Published: 2022-05-01 17:27:05


TypeScript gains popularity each day. We use TypeScript because it makes development safer and faster. Here is a set of tricks and guidelines for a more rigorous use of TypeScript. You need to get used to them once - and they will save a lot of time in the future.



## Do not use `any`

The trick is, instead of ignoring the problem - "legalize" it:
`type TODO_ANY = any`

#### Pros:
• You do not have to be desperate and write props.
• You are deliberately showing that the code should be improved in the future.
• This type is easy to find by searching for the project.

#### Restrictions:
 • Using this technique too often is unlikely to be beneficial.
• Conscientiousness and responsibility are needed to remove such gaps in the future.

You may also find it useful:
`export type TODO_UNKNOWN = unknown;`
 
In the case of any, the compiler will allow almost any action. In the case of unknown, on the contrary, almost any use will lead to an error.

Last word, do not use `any` even if type does not matter, for example, instead of this:
   `const getLength = (arr: any []) => arr.length`

you can write this:
   `const getLength = (arr: { length: number }) => arr.length`
It is not ideal, but is much better than `any`. 

Of course best solution in this specific case is to use generics:
`const getLength = <T>(arr: T[]) => arr.length;`



## Use `readonly`
It is bad practice to mutate the structures you work with. For example, in the Angular world, this immediately leads to a number of consequences in the application: problems appear with checking changes in components, after which the display is not updated after mutating the data.
But can we easily prevent all attempts to mutate data? For myself, I just formed a habit of writing readonly everywhere.

#### What is to be done?
There are likely many places in your application where you can replace unsafe types with readonly alternatives.

Use _readonly_ on interfaces:

```
// Before
export interface Person {
  name: string;
}

// After
export interface Person {
  readonly name: string;
}

```
Give preference to _readonly_ in the types:
```
// Before
export type UnsafeType = { prop: number }

// After
export type SafeType = Readonly<{ prop: number }>
```
Use _readonly_ fields class wherever it is possible:
```
// Before
class UnsafeComponent {
  loader$ = new Loader<boolean>(true);
}

// After
class SafeComponent {
  readonly loader$ = new Loader<boolean>(true);
}
```

You can use _readonly_ on all data structures, like:
`const numsFromServer = readonly number[] = [1, 2, 42]`


#### as const
TypeScript v3.4 introduces const-assertions. It is a stricter tool than readonly types because it will pack your constant with the most precise type possible. Now you can be sure: no one and nothing can change this.

Also, when using as const, your IDE will always show you the exact type of the entity being used.



## Use _strict_ mode
TypeScript has a cool strict mode that is unfortunately disabled by default. It includes a set of rules for safe and comfortable working with TypeScript.
With _strict_ mode, you forget about errors like **_undefined is not a function_** and _**cannot read property X of null**_. Your types will be accurate and correct.

#### What is to be done?
If you start a new project, just turn on _strict_ right away and be happy.

If you already have a project without _strict_ mode and now you want to enable it, then you will face a number of difficulties. It is very difficult to write _strict_ code when the compiler does not signal problems in any way. So you will probably have a lot of buggy spots and the migration process makes you bored very quickly.

Generally, the best approach is to break this huge task into chunks. The _strict_ mode itself is a set of six rules. You can enable one of them and fix any errors. Your project is now a little more rigorous. Next time, turn on another rule, correct any mistakes and keep working. In one day, you will collect the entire _strict_ mode!

But in large projects everything is not so smooth, then you can act more iteratively. Enable the flag, and above all conflicts, add `@ts-ignore` and a TODO comment. The next time you work with the file, correct the type as well.

Setting --`strict` to true, sets all of the following options to true:

* --_noImplicitAny_: If TypeScript can’t infer a type, we must specify it. This mainly applies to parameters of functions and methods: With this settings, we must annotate them.
* --_noImplicitThis_: Complain if the type of this isn’t clear.
* --_alwaysStrict_: Use JavaScript’s strict mode whenever possible.
* --_strictNullChecks_: null is not part of any type (other than its own type, null) and must be explicitly mentioned if it is a acceptable value.
* --_strictFunctionTypes_: enables stronger checks for function types.
* --_strictPropertyInitialization_: Properties in class definitions must be initialized, unless they can have the value undefined.



## Use _Utility Types_
TypeScript has a set of types that are shortcuts for conversions of other types.

I advise you to study in detail all the official documentation on [_Utility Types_](https://www.typescriptlang.org/docs/handbook/utility-types.html) and start actively introducing them into your applications. They also save a lot of time.

#### From my point of view, most useful utility types are Pick and Omit, they allow you to avoid creating not essential intermediate types.

#### `Pick<Type, Keys>`
```
interface Todo {
  title: string;
  description: string;
  completed: boolean;
}
 
type TodoPreview = Pick<Todo, "title" | "completed">;
 
const todo: TodoPreview = {
  title: "Clean room",
  completed: false,
};
```

#### `Omit<Type, Keys>`
```
interface Todo {
  title: string;
  description: string;
  completed: boolean;
  createdAt: number;
}
 
type TodoInfo = Omit<Todo, "completed" | "createdAt">;
 
const todoInfo: TodoInfo = {
  title: "Pick up kids",
  description: "Kindergarten closes at 5pm",
};
```


##  Conclusion
These are just a few of the principles we use, but in our team, they are perhaps the most basic. Of course, this is not the only correct way to write code in TypeScript, but once we get used to it, we just write the code this way without thinking. From time to time, this saves us from unexpected tricky mistakes in large projects.