-
Naming Style ⬆
Category | Style |
---|---|
class | UpperCamelCase |
interface | UpperCamelCase |
type | UpperCamelCase |
enum | UpperCamelCase |
variable | lowerCamelCase |
function | lowerCamelCase |
method | lowerCamelCase |
property | lowerCamelCase |
enum values | lowerCamelCase |
module alias | lowerCamelCase |
global constant values | CONSTANT_CASE |
static readonly values | CONSTANT_CASE |
- Prefixes are forbidden.
- Generics parameters like
Array<T>
should use a single uppercase character.
-
Braces ⬆
- Braces are required for all control structures (i.e. if, else, for, do, while, as well as any others), even if the body contains only a single statement.
- For nonempty code blocks use "Egyptian brackets"
- No line break before the opening brace.
- Line break after the opening brace.
- Line break before the closing brace.
- Line break after the closing brace if that brace terminates a statement or the body of a function or class statement, or a class method. Specifically, there is no line break after the brace if it is followed by else, catch, while, or a comma, semicolon, or right-parenthesis.
class SomeClass { constructor() {} public someMethod(param: boolean): void { const obj = {}; if (param) { try { doSomething(); } catch (err: Error) { handleError(err); } } } }
- An empty block construct may be closed immediately after it is opened, with no symbols in between
{}
, unless it is a part of a multi-block statement (one that directly contains multiple blocks:if/else
ortry/catch/finally
).
-
Indentation +2 ⬆
- Each time a new block construct is opened, the indent increases by 2 spaces.
- In switch statement after a switch label a new line should be inserted and the indentation is increased by +2.
-
Statements ⬆
- Each statement is followed by a line break.
- Every statement must be terminated with a semicolon. Relying on automatic semicolon insertion is forbidden.
-
Column limit: 80 ⬆
- If a line is longer than 120 symbols it should be wrapped.
- Line break should be placed after the operator. This does not apply to the 'dot' (which is not an operator).
- Break lines at a higher syntactic level.
- The syntactic levels from highest to lowest are as follows: assignment, division, function call, parameters, number constant.
- Indent continuation lines at least +4 spaces from the original line.
-
Vertical Whitespace ⬆
- A single blank line appears:
- Between methods in a class or object literal. A blank line between two consecutive properties is optional. Such blank lines could be used for logical separation.
- Inside methods bodies for logical groupings of statements. Do not put blank lines at the beginning and end of the method.
- After import section, between class definitions.
- Optionally before the first method in a class or object literal.
- Optionally between properties in a class or object literal for logical grouping.
- Multiple blank lines are permitted, but never required (nor encouraged).
- A single blank line appears:
-
Horizontal whitespace ⬆
The use of horizontal whitespace depends on location and falls into three broad categories: leading (at the start of a line), trailing (at the end of a line), and internal. Leading whitespace (i.e., indentation) is addressed here. Trailing whitespace is forbidden.
A single internal space appears in the following places:
- Separating any reserved word (such as
if
,for
, orcatch
) except forfunction
andsuper
, from an open parenthesis ((
) that follows it on that line. - Separating
case
from a closing curly brace (}
) that precedes it on that line. - After opening curly brace
{
and before closing}
curly brace when it's a single property object literal:{ name: 'John' }
,{ lastname }
,{ ...data }
. - After opening bracket
[
and before closing]
bracket:[ 4, 5, 6 ]
,[ ...data ]
. - Before any open curly brace (
{
), with two exceptions:- Before an object literal that is the first argument of a function or the first element in an array literal (e.g.
foo({ a: [{ c: d }] })
). - In a template expansion, as it is forbidden by the language (e.g. valid:
ab${1 + 2}cd
, invalid:xy$ {3}z
).
- Before an object literal that is the first argument of a function or the first element in an array literal (e.g.
- On both sides of any binary or ternary operator.
- After a comma (
,
) or semicolon (;
). Note that spaces are never allowed before these characters. - After the colon (
:
) in an object literal.
- Separating any reserved word (such as
-
Visibility ⬆
- Limit visibility of properties, methods and types as much as possible.
- Always use visibility modifiers as
public
,private
,protected
.
-
Constructors ⬆
- Omit empty constructors or one that simply delegates into its parent class. However, constructors with parameters should not be omitted even if the body of the constructor is empty.
-
Class Members ⬆
- Initialize class property where it's declared.
- Getter should be a pure function. Do not create trivial getters and setters (without any logic or decorators).
-
Primitive Types & Wrapper Classes ⬆
- Do not use wrapper classes for the primitive types like
Boolean
,String
.
- Do not use wrapper classes for the primitive types like
-
Objects ⬆
- Use trailing commas whenever there is a line break between the final property and the closing brace.
- Use the literal syntax for object creation:
const obj = {};
- Use property shorthand syntax.
const firstName = 'Boris'; const lastName = 'Blade'; const user = { firstName, lastName, };
-
Strings ⬆
- Use single quotes for strings.
- When building up strings use template strings instead of concatenation.
const str = `Hello ${user}!`;
-
Arrays ⬆
- Do not use
Array()
constructor. Instead, always use brackets to initialize arrays. - Include a trailing comma whenever there is a line break between the final element and the closing bracket.
- Do not use
-
Variables ⬆
- Always use
const
orlet
to declare variables. Useconst
by default, unless a variable needs to be reassigned. Never usevar
.
- Always use
-
Exceptions ⬆
- Always throw
Error
or subclass ofError
. Usenew Error()
when instantiating exceptions, instead of just callingError()
.
- Always throw
-
Iterating and loops ⬆
- Do not use
for (... in ...) to
iterate over arrays. It will counterintuitively give the array's indices (as strings!), not values. Usefor (... of someArr)
or vanilla for loops with indices to iterate over arrays.
- Do not use
-
Switch statement ⬆
- All
switch
statements must contain adefault
statement, even if it contains no code. - Each case in a
switch
statement must be wrapped in a{}
block.
- All
-
Equality Checks ⬆
- Always use triple equals
(===)
and not equals(!==)
. - Use double equals
(==)
only to catch bothnull
andundefined
. In this case all other falsy values like0
,false
does not catch.
- Always use triple equals
-
Function Declaration ⬆
- Use regular function declaration
function someFunction() {...}
to declare named functions. - Use arrow function properties
const someFunction = () => {...}
instead of rebindingsomeFunction.bind(this)
. - Use arrow functions to call instance methods:
setTimeout(() => { this.someFunction() }, 5000)
- Use regular function declaration
-
Event Handlers ⬆
- Use arrow function properties as event handlers. It's possible to use arrow functions as event handlers if there is no need tp uninstall the handler.
-
@ts-ignore ⬆
- Try not to use
@ts-ignore
. Try to avoid@ts-ignore
when dealing with type error. Advices from Type any could help.
- Try not to use
-
Decorators ⬆
- When using decorators, the decorator must immediately precede the symbol it decorates, with no empty lines between.
- Decorators should be on the line above the decorated class, property or method:
@injectable() class UserService { @observable private username = ''; }
-
Non-nullability Assertions ⬆
- You should not use type and non-nullability assertions without an obvious or explicit reason for doing so. Instead of
(x as Foo).foo(); y!.bar();
Use the following code. It will produce runtime check and code will not crash.
if (x instanceof Foo) { x.foo(); } if (y) { y.bar(); }
- Use optional chaining
?.
operator for "presence" (if value is notnull
orundefined
) check.
// bad if(user && user.address && user.address.street) //good if(user?.address?.street)
- Use nullish coalescing
??
operator as "fall back" to a default value when dealing withnull
orundefined
.
-
File Names ⬆
- File name should be in
lowerCamelCase
format. File name may have dot separator (.
) and details of file type:users.api.tsx
- File name should be in
-
Modules ⬆
- Use named exports. Do not use default exports.
- Use destructing imports by default. You may use also use renaming imports if necessary.
-
Type Inference ⬆
- Type annotations for variables, fields, etc should improve code readability.
Do not annotate trivially inferred types:
string
,number
,boolean
ornew
expression. For example:
const x = 5; // Clearly number // Compare: const s1: Set<string> = new Set<string>(); const s2 = new Set<string>(); // Again, type of s2 is simply recognized and more readable.
- Return types for functions and methods should be always explicitly annotated.
- Type annotations for variables, fields, etc should improve code readability.
Do not annotate trivially inferred types:
-
Null and Undefined ⬆
- There is no rule to use
null
overundefined
or vice versa. Use what suits better for particular case. - Types must not include
|null
or| undefined
in a union type. Instead oftype Direction = Left | Right | undefined
useDirection | undefined
- Use optional
someFunction(param?: ParamType)
fields and parameters instead of defining| undefined
type. For classes preferably avoid this pattern altogether and initialize as many fields as possible.
- There is no rule to use
-
Interface and Type Aliases ⬆
- Interfaces and Type aliases are almost identical. So in order to write code in the same style let's use one approach. Use interfaces over the type aliases.
-
Arrays ⬆
- For simple types (single shor type name) use
T[]
syntax. For longer and complex forms useArray<T>
const a1: string[]; const a2: User[]; // but const a3: Array<number | undefined>
- For simple types (single shor type name) use
-
Type
any
⬆- Consider not to use
any
. In circumstances where you want to use any, consider one of:- Provide a more specific type. Use interface, an inline object type, or a type alias
- Use
unknown
- Suppress the lint warning and document why it is legitimate
- Take a look at the recommendation here.
- Consider not to use
-
Code style principles ⬆
- Be consistent. If some questions are not covered in the list above, format code the same way as it formatted in other files of a project.
- Write new code in standardized style unless this will introduce huge changes in linting.
-
File layout ⬆
Typical file layout should have the following order:
- Import statements. Imports from the libraries should be above the local imports;
- File constants (not class constants);
- Interface and type definitions;
- Class definition;
- Export statement;
Class layout:
- public properties
- protected properties
- private properties
- constructors
- public methods
- protected methods
- private methods