ttt

Quick Tips

This page lists various tips related to type annotations. Note that tooling and framework tips are out of scope for this document.

Never use any

Using any is basically disabling type checking - which is probably the opposite of your goal when you’re using TypeScript. It should only be used as a last resort, when the alternatives (if any exist) are even less desirable.

For example:

Avoid conditional types

Just because a feature exists doesn’t mean you should use it for everything.

See alternatives for common usages of conditional types below.

Simple conditional types

You may be able to use overloads:
Open in Playground

function foo<T extends string | number>(it: T): T extends string ? 'string' : 'number' {}

function foo2() {
  //
}

Remember to use readonly T[] in function parameters

You should often be using readonly T[] in function parameters if you aren’t modifying them. This is because readonly T[] is a parent type of T[], meaning a readonly T argument accepts both readonly T and T, whereas a T argument doesn’t accept a readonly T.

(Note: { t: 't'; } and { readonly t: 't'; } are both equivalent in the type system. See pc#6532, i#13347)

You can think of it like this:
Open in Playground

type T = {
  read(): 't';
  write(t: 't'): void;
};
type ReadonlyT = {
  read(): 't';
};
type ReadonlyExtendsNonReadonly = ReadonlyT extends T ? true : false;
//   ^? - type ReadonlyExtendsNonReadonly = false
type NonReadonlyExtendsReadonly = T extends ReadonlyT ? true : false;
//   ^? - type NonReadonlyExtendsReadonly = true

Use T extends T instead of T extends any or T extends unknown for distributive conditional types

They all always succeed, but T extends T has some benefits for maintainability: