3. JavaScript and TypeScript guidelines
3.1. Source Files
Section titled “3.1. Source Files”3.1.1. File Names
Section titled “3.1.1. File Names”File names must be all lowercase and may include underscores (_), but no additional punctuation.
Follow the convention that your project uses. Filenames extension must be .js or .ts.
3.1.2. Double File Extensions
Section titled “3.1.2. Double File Extensions”To separate internal file types it’s recommended to add second extension to the file name.
For example, type.ts for TypeScript types or .spec.js for JavaScript test files.
Do not use double file extensions for React and Vue components. Instead, split the file into multiple files with the same name but different extensions.
3.2. ECMAScript Modules
Section titled “3.2. ECMAScript Modules”All JavaScript source files must be written using ES modules.
ES modules are files that use the import and export keywords.
3.3. Import and Export Statements Order
Section titled “3.3. Import and Export Statements Order”import and export statements must be sorted in the following order:
importstatementsexportstatements
In case of re-exporting a module, import and export statements must be separated by an empty line.
Recommended
import { useState } from 'react';
export { useState };Not recommended
export { useState };import { useState } from 'react';3.4. Import Statements
Section titled “3.4. Import Statements”3.4.1. Import Syntax
Section titled “3.4.1. Import Syntax”Always use single quotes for import paths.
Recommended
import fs from 'node:fs';Not recommended
import fs from "node:fs";3.4.2. Import Statements Grouping
Section titled “3.4.2. Import Statements Grouping”import statements must be sorted in the following order:
- Globally scoped source and type files (e.g.
bun,node:,@types/node) - External modules (e.g.
react,react-dom,@types/react,@types/react-dom) - Application level modules (e.g.
@/components,@/utils,@/hooks,@/types) - Local modules or files referred by its relative path (e.g.
../components,../../constants,../utils,../types) - Current module files (e.g.
./interface.type,./constants,./enums)
Recommended
import { path } from 'node:fs';import { useState } from 'react';import Button from '@/components/button';import { BUTTON_WIDTH } from '../constants/button.const';import ButtonIcon from './button_icon';Not recommended
import { Button } from '@/components/button';import ButtonIcon from './button_icon';import { BUTTON_WIDTH } from '../constants/button.const';import { path } from 'node:fs';import { useState } from 'react';3.4.3. Import Statements Sorting and Line Wrapping
Section titled “3.4.3. Import Statements Sorting and Line Wrapping”import statements must be sorted alphabetically within each group. type prefix should be ignored when sorting.
import statements must be wrapped to the next line if they import more than one module.
Recommended
import { path, stat,} from 'node:fs';import { useState, type ReactNode,} from 'react';import Button, { type ButtonEmits, type ButtonProps,} from '@/components/button.vue';import { BUTTON_WIDTH } from '../constants/button.const';Not recommended
import { path, stat } from 'node:fs';import { useState, type ReactNode } from 'react';import Button, { type ButtonEmits, type ButtonProps } from '@/components/button.vue';import { BUTTON_WIDTH,} from '../constants/button.const';3.4.4. Import Namespaces
Section titled “3.4.4. Import Namespaces”Do not import namespaces, if possible. Instead, import the specific symbols you need.
Recommended
import { path } from 'node:fs';Not recommended
import * as fs from 'node:fs';3.4.5. File Extensions in Import Paths
Section titled “3.4.5. File Extensions in Import Paths”The .js, .jsx, .ts and .tsx file extensions must always be excluded.
The .vue file extension must always be included when importing a single component.
Recommended
import Button from '@/react/components/button';import Button from '@/vue/components/button.vue';Not recommended
import Button from '@/react/components/button.tsx';import Button from '@/vue/components/button';3.5. Export Statements
Section titled “3.5. Export Statements”3.5.1. Export Statements Sorting and Line Wrapping
Section titled “3.5.1. Export Statements Sorting and Line Wrapping”Exported modules must be sorted alphabetically within each group. type prefix should be ignored when sorting.
Re-exported modules must be placed at the beginning of the exports statement, before other exported modules.
Default export must be placed at the end of the exports statement.
Exported modules must be wrapped to the next line if they export more than one module.
Separate export groups with an empty line (optional).
Recommended
export { BUTTON_WIDTH } from './constants/button.const';
export const BUTTON_HEIGHT = 40;
export { path, type ReactNode, stat, useState,};
export type { ButtonEmits, ButtonProps,};
export default Button;Not recommended
export default Button;export { BUTTON_WIDTH } from './constants/button.const';export { path, stat };export type { ReactNode,};export const BUTTON_HEIGHT = 40;3.5.2. Export Visibility
Section titled “3.5.2. Export Visibility”JavaScript and TypeScript does not support restricting the visibility for exported symbols. Only export symbols that are used outside of the module. Generally minimize the exported API surface of modules.
3.5.3. Export Mutable Variables
Section titled “3.5.3. Export Mutable Variables”Mutable variables should not be exported. It can create hard to understand and debug code, in particular with re-exports across multiple modules.
Recommended
let BUTTON_HEIGHT = 40;
// Somewhere in the codeBUTTON_HEIGHT = 50;
export const getButtonHeight = () => { return BUTTON_HEIGHT;};Not recommended
export let BUTTON_HEIGHT = 40;
// Somewhere in the codeBUTTON_HEIGHT = 50;3.5.4. Container classes
Section titled “3.5.4. Container classes”Do not create container classes with static methods or properties for the sake of namespacing.
Recommended
export const BUTTON_WIDTH = 40;
export function click() { console.log('Button clicked');}Not recommended
export class Button { static WIDTH = 40; static click() { console.log('Button clicked'); }}3.6. Formatting
Section titled “3.6. Formatting”Refer to the General formatting guidelines for the general formatting rules.
3.6.1. Quotes
Section titled “3.6.1. Quotes”Always use single quotes for strings.
Recommended
const foo = 'bar';Not recommended
const foo = "bar";3.6.2. Special Escape Sequences
Section titled “3.6.2. Special Escape Sequences”For any character that has a special escape sequence (\', \", \\, \b, \f, \n, \r, \t, \v),
that sequence is used rather than the corresponding numeric escape (e.g \x0a, \u000a, or \u{a}).
Legacy octal escapes are never used.
3.6.3. Non-ASCII Characters
Section titled “3.6.3. Non-ASCII Characters”For the remaining non-ASCII characters, either the actual Unicode character (e.g. ∞) or the equivalent hex or Unicode escape (e.g. \u221e) is used,
depending only on which makes the code easier to read and understand.
Recommended
const unit = 'μs';const unit = '\u03bcs'; // 'μs'const millisecond = '\u03bcs';Not recommended
const unit = '\u03bcs';3.6.4. Statements
Section titled “3.6.4. Statements”Each statement is followed by a line-break.
Recommended
const a = 1;const foo = 'bar';Not recommended
const a = 1; const foo = 'bar';3.6.5. Semicolons
Section titled “3.6.5. Semicolons”Semicolons are required for all statements.
Recommended
const a = 1;Not recommended
const a = 13.6.6. Braces
Section titled “3.6.6. Braces”Braces are required for all control structures (i.e. if, else, for, do, while, etc.).
Exception: a statement (including arrow function declaration) that fits on a single line with no wrapping.
Recommended
if (condition) { console.log('condition is true');} else { console.log('condition is false');}
const foo = () => { console.log('foo'); return 'foo';};
const bar = (x: number) => x * 2;Not recommended
if (condition) console.log('condition is true');else console.log('condition is false');
const foo = () => { console.log('foo'); return 'foo'; };
while (condition) console.log('condition is still true');3.6.7. Empty Blocks
Section titled “3.6.7. Empty Blocks”An empty block may be closed immediately after it is opened, with no characters, space, or line break in between.
const doNothing = () => {};
try { doSomething();} catch {}3.7. JavaScript Language Features
Section titled “3.7. JavaScript Language Features”3.7.1. Variables
Section titled “3.7.1. Variables”3.7.1.1. Local Variable Declarations
Section titled “3.7.1.1. Local Variable Declarations”Always use const and let for local variable declarations. Avoid using var.
const and let are block scoped, like variables in most other languages.
var in JavaScript is function scoped, which can cause difficult to understand bugs. Don’t use it.
Recommended
const name = 'John';let age = 20;Not recommended
var name = 'John';var age = 20;3.7.1.2. One Variable per Declaration
Section titled “3.7.1.2. One Variable per Declaration”Declare one variable per declaration.
Separate each declaration with a newline.
Recommended
let name = 'John';let age = 20;Not recommended
let name = 'John', age = 20;let name = 'John'; let age = 20;3.7.1.3. Do not use the Array constructor
Section titled “3.7.1.3. Do not use the Array constructor”Do not use the Array() constructor, with or without new.
Instead, always use bracket notation to initialize arrays, or from() to initialize an Array with a certain size.
Recommended
const a = [2];const b = [2, 3];
const c = [];c.length = 2;
const d = Array.from<number>({ length: 5 }).fill(0);Not recommended
const a = new Array(2); // [undefined, undefined]const b = new Array(2, 3); // [2, 3];3.7.1.4. Array Literal Syntax
Section titled “3.7.1.4. Array Literal Syntax”Always use the array literal syntax ([]) for array initialization.
Wrap each array element on a new line and add a trailing comma, unless the array is empty or contains only one element.
Recommended
const a = [ 2, 3,];Not recommended
const a = new Array(2, 3);const b = [2, 3];3.7.1.5. Object Literal Syntax
Section titled “3.7.1.5. Object Literal Syntax”Always use the object literal syntax ({}) for object initialization.
Always wrap each object property on a new line and add a trailing comma.
Recommended
const a = { name: 'John', age: 20,};const b = { count: 1,};Not recommended
const a = { name: 'John', age: 20 };const b = { count: 1 };3.7.2. Functions
Section titled “3.7.2. Functions”3.7.2.1. Arrow Functions
Section titled “3.7.2.1. Arrow Functions”Arrow functions provide a concise function syntax and simplify scoping this for nested functions. Prefer arrow functions over the function keyword for nested functions.
Prefer arrow functions over other this scoping approaches such as f.bind(this) or const self = this.
3.7.2.2. Nested Functions
Section titled “3.7.2.2. Nested Functions”Functions may contain nested function definitions. If it is useful to give the function a name, it should be assigned to a local const.
3.7.2.3. Function Arguments
Section titled “3.7.2.3. Function Arguments”Prefer to wrap function arguments by a new line, unless the function has only one argument or all arguments are perfectly fits on a single line.
Recommended
function foo( email: string, firstName: string, lastName: string, age: number,): void { // ...}
const bar = (email: string): void => { // ...};Not recommended
function foo(email: string, firstName: string, lastName: string, age: number) {}For arrow functions, omit the parentheses if the function has only one argument and its type is already defined (e.g., for callback functions).
Recommended
userService.onSignUp(user => { // ...});Not recommended
userService.onSignUp((user: User) => { // ...});3.7.2.3. Function Arguments as Object Literal
Section titled “3.7.2.3. Function Arguments as Object Literal”For those functions that accept a lot of arguments, prefer to pass them as an object literal, rather than positional arguments.
Recommended
function foo(user: { email: string; firstName: string; lastName: string; dateOfBirth: Date; country: string; city: string; zip: string;}): void { // ...}Not recommended
function foo( email: string, firstName: string, lastName: string, dateOfBirth: Date, country: string, city: string, zip: string,): void { // ...}3.7.3. Classes
Section titled “3.7.3. Classes”3.7.3.1. Class Declaration
Section titled “3.7.3.1. Class Declaration”Always use the class declaration syntax (class) for class declarations.
Do not add semicolons after methods, or after the closing brace of a class declaration. Statements, such as assignments, that contain inline class expressions are still terminated with a semicolon.
Recommended
class Button { constructor(name) { this.name = name; }}Not recommended
class Button { constructor(name) { this.name = name; }}3.7.3.2. Class Instantiation
Section titled “3.7.3.2. Class Instantiation”Always use the new operator to instantiate a class, rather than calling the class as a function.
Also, add parentheses to the class name.
Recommended
const button = new Button();Not recommended
const button = Button();const button = new Button;3.7.4. Switch Statements
Section titled “3.7.4. Switch Statements”3.7.4.1. Case Statements
Section titled “3.7.4.1. Case Statements”Wrap each switch case statement by a new line.
Add an extra empty line between break and the following case statement.
Exception: if all case statements are returning a value, the extra empty line, and the break statement, are not needed.
Recommended
switch (condition) { case 'a': console.log('a'); break;
case 'b': console.log('b'); break;
default: console.log('default'); break;}
switch (condition) { case 'a': return 'a'; case 'b': return 'b';}Not recommended
switch (condition) { case 'a': console.log('a'); break; case 'b': console.log('b'); break; default: console.log('default'); break;}3.7.4.2. Fall-through
Section titled “3.7.4.2. Fall-through”In case of fall-through, add a comment to the case statement to indicate that the fall-through is intentional.
switch (condition) { case 'a': console.log('a'); // fall through case 'b': console.log('b'); break;}3.7.5. Template Literals
Section titled “3.7.5. Template Literals”Prefer template literals over complex string concatenation, particularly if multiple string literals are involved.
Template literals may span multiple lines.
If a template literal spans multiple lines, it does not need to follow the indentation of the enclosing block, though it may if the added whitespace does not matter.
const foo = ` bar baz qux`;3.8. TypeScript Language Features
Section titled “3.8. TypeScript Language Features”3.8.1. Type Annotations
Section titled “3.8.1. Type Annotations”Prefer to use type annotations over type inference, particularly for complex types.
Exception: if the type is trivially inferred from the context, it is not necessary to annotate it.
Recommended
const foo = 'bar';const user: { email: string; firstName: string; lastName: string;} = { // ...};Not recommended
const foo = 'bar';const user = { // ...};3.8.2. Return Types
Section titled “3.8.2. Return Types”Always annotate the return type of a function. If the function does not return a value, annotate the return type as void.
Recommended
function foo(): string { return 'bar';}
function bar(): void { console.log('bar');}Not recommended
function foo() { return 'bar';}
function bar() { console.log('bar');}3.8.3. Arguments
Section titled “3.8.3. Arguments”Prefer optional over |undefined, unless the argument is required.
Recommended
const foo = (bar?: string): void => { // ...};
const bar = (foo: string | undefined, bar?: string): void => { // ...});Not recommended
const foo = (bar: string | undefined): void => { // ...};3.8.4. Classes and Interfaces
Section titled “3.8.4. Classes and Interfaces”Use TypeScript interfaces to define structural types, not classes.
Recommended
type User = { email: string; firstName: string; lastName: string;}
const user: User = { // ...};Not recommended
class User { readonly email: string; readonly firstName: string; readonly lastName: string;}
const user: User = { // ...};3.8.4. interface vs type
Section titled “3.8.4. interface vs type”Prefer using interface over type for type declarations. The reason is that interface is more flexible and easier to extend.
Also, there are some performance issues with type when using it with generics.
Recommended
type User = { email: string; firstName: string; lastName: string;}Not recommended
interface User { email: string; firstName: string; lastName: string;}3.8.4. Arrays and Tuples
Section titled “3.8.4. Arrays and Tuples”For trivial types, use the syntax sugar T[] for typing arrays, and [T, T] for typing tuples.
For complex types, use the longer equivalent Array<T> syntax.
Prefer using objects over tuples, since it’s often clearer to provide meaningful names for the properties
3.8.5. any and {} Types
Section titled “3.8.5. any and {} Types”Prefer using unknown over any (or {} which is equivalent to any for objects) type, unless it is absolutely necessary. In this case, add a comment to explain why.
Recommended
const validate = (value: unknown): boolean => { // ...};Not recommended
const validate = (value: any): boolean => { // ...};3.8.6. Generics
Section titled “3.8.6. Generics”Prefer using generic types over type unions to create reusable types.
Recommended
const foo = <T extends string | number>(value: T): T => { return value;};Not recommended
const foo = (value: string | number): string | number => { return value;};3.8.7. @ts-ignore
Section titled “3.8.7. @ts-ignore”Do not use @ts-ignore nor the variants @ts-expect-error or @ts-nocheck.
It may seem like an easy way to “fix” a compiler error, but in practice, a specific compiler error is often caused by a larger problem that can be fixed more directly.
// @ts-ignoreconst foo: string = 5;