This page lists various tips related to type annotations. Note that tooling and framework tips are out of scope for this document.
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:
Just because a feature exists doesn’t mean you should use it for everything.
See alternatives for common usages of conditional types below.
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() {
//
}
readonly T[]
in function parametersYou 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
T extends T
instead of T extends any
or T extends unknown
for distributive conditional typesThey all always succeed, but T extends T
has some benefits for maintainability:
any
makes it easier to limit your usage of any
- this also makes linters happyT
is shorter than both any
and unknown
-
of course, this doesn’t apply if you use long names for your generic parametersT
always extends T
-
you don’t even need to know what any
and unknown
are