This style guide extends existing JavaScript style guide and defines rules for TypeScript specific features.
tsc
has some options
to make type checks stricter:
{
"compilerOptions": {
"strict": true,
"noEmitOnError": true
}
}
It's recommended to turn them on as early as possible in development (and migration from JS).
If you want TypeScript
to check for unused variables and parameters instead tslint
, you can
add the following options:
{
"compilerOptions": {
"noUnusedLocals": true,
"noUnusedParameters": true
}
}
-
InterfaceLikeThis
. -
TypeAliasLikeThis
. -
In generics:
K
andV
are reserved for key-value generic data structures,K
for key types andV
for value types;T
andU
are reserved for generic data types;- other generic parameters should have meaningful names:
Good:
function foo<Attribute, State>(attributes: Attribute[], state: State): void { // ... }
Bad:
function foo<A, S>(attributes: A[], state: S): void { // ... }
-
Prefer interfaces to in place structure definitions:
Explanation: In place structure definitions make it unnecessary difficult to reuse a type. Also meaningful type names help to document code.
Good:
interface Person { name: string; age: number; } let person: Person;
Bad:
let person: {name: string, age: number};
-
Prefer interfaces to type aliases.
Explanation: From handbook:
...interfaces create a new name that is used everywhere... [whereas] ...type aliases don’t create a new name — for instance, error messages won’t use the alias name.
...type aliases cannot be extended or implemented from.
-
Don't use
void
for functions returningundefined
:Explanation:
void
means that a function doesn't return a value;void
isn't assignable toundefined
.
Good:
function find(): object | undefined { // ... return; // or `return undefined;` for the sake of being explicit. }
Bad:
function find(): object | void { // ... }
-
There should be one whitespace before and after the type operator:
Good:
let foo: string | number;
Bad:
let foo: string|number;
-
Use
as Type
for type assertions:Explanation: Use one style for
JSX
and not.Good:
(someValue as string).length;
Bad:
(<string>someValue).length;
-
For array declaration use
T[]
notation:Good:
let pool: (LockableConnection | null)[];
Bad:
let pool: Array<LockableConnection | null>;
-
Don't use
public
modifier for public class members:Explanation: In TypeScript, each member is public by default (like in JavaScript). Explicit public modifier is redundant.
Good:
class Foo { publicProp: number; publicMethod(): void {} }
Bad:
class Foo { public publicProp: number; public publicMethod(): void {} }
-
Prefix
private
andprotected
fields with_
:Explanation:
- Underscore specifies visual difference from public fields. So, you don't need to read the field's declaration to determine its visibility.
- Minimizes name conflicts with public fields: after adding public field with the same name as private field, you don't need perform renaming.
- Simplifies migration from JavaScript, where
_
often means that the field is "internal". - Improves autocompletion by dictionary: when you type
_
the autocomplete popup shows non-public fields.
Good:
class Foo { private _prop: number; protected _method(): void {} }
Bad:
class Foo { private prop: number; protected method(): void {} }
-
Prefer using const enums.
Explanation: const enums are completely removed during compilation. Const enum members are inlined at places of use.
If you need real objects in generated code, use non-const enums. E.g.:
- enum contains computed members;
- enum should be printed (generated object stores both forward (
name -> value
) and reverse (value -> name
) mappings); - enum should be available from JavaScript.
-
For enum values use
UPPER_CASE
style.Good:
enum Foo { BAR, BAZ } enum Bar { FOO = 'foo', BAZ = 'baz' }
Bad:
enum Foo { Bar, Baz }