TypeScript
Coding standards for TypeScript code.
Scopeβ
Be restrictive by defaultβ
By default, keep your public API (the parts of your code that are accessible from an outside scope) as restrictive as possible.
- Use a private scope for properties and methods by default.
- Only expose properties and methods when necessary (protected if possible, public otherwise).
- Make class properties
readonlyif possible.
Use # to make members privateβ
Always use the # prefix for private class members.
// π« Bad: not restrictive and scoped incorrectly
class Car {
protected isDrivingSubject = new BehaviorSubject(false);
public isDriving$ = this.isDrivingSubject.asObservable();
private isDriving = false;
}
// β
Good: restrictive and scoped correctly
class Car {
protected readonly isDrivingSubject = new BehaviorSubject(false);
readonly isDriving$ = this.isDrivingSubject.asObservable();
#isDriving = false;
}
This also allows you to use the same name for public getters that map to private members:
class MyClass {
#isEnabled = false;
get isEnabled() {
return this.#isEnabled;
}
}
Class member orderβ
- Place properties up top followed by methods.
- Place private members after public members, alphabetized.
Use type inferenceβ
Use type inference whenever you can to keep your scripts clutter free and prevent prevent unused side effects when types lie to you.
Keep your code clutter freeβ
// π« So much clutter
const subject: BehaviorSubject<SomeItemInterface> = new BehaviorSubject<Array<SomeItemInterface>>([]);
// β
Super short and nice
const subject = new BehaviorSubject<Array<SomeItemInterface>>();
Use as constβ
Using a return type can lead to unwanted side-effects, that are also not easily spotted during a code review. Please use as const as this returns the exact values as constants, so youβd know exactly what is being returned.
type Person = {
name: string;
age: number;
};
// π« Bad: return type and not as const
function sarah(): Person {
return {
name: 'Sarah',
age: 23,
};
}
// β
Good: no return type, includes as const
function sarah() {
return {
name: 'Sarah',
age: 23,
} as const;
}
Naming conventionsβ
Use plural or singular namesβ
Classes, interfaces, types and enums should always have a singular name.
// π« Bad: plural name for enum
enum EmployeeTypes {
FullTime,
PartTime,
}
// β
Good: singular name for enum
enum EmployeeType {
FullTime,
PartTime,
}
Arrays or other list types should have a plural name, except if they end with Map, List or Collection.
// π« Bad
const item: Array<Item>;
const itemsList: Array<Item>;
// β
Good
const items: Array<Item>;
const itemList: Array<Item>;
Capitalize abbreviations and acronyms like wordsβ
We treat abbreviations and acronyms like words. This means only the first character is capitalized when using camelCase or PascalCase.
// Bad: all caps abbreviations
const JSONAPIURL = new JSONAPIURL();
class XIPConfig () {}
// Good: correct casing for abbreviations
const jsonApiUrl = new JsonApiUrl();
class XipConfig () {}
Donβt include redundant contextβ
A name should make sense in its context. Omit redundant words from names that can be inferred from the context.
// π« Bad: uses prefixes that can be induced from the context
const user = {
userFirstName: 'John',
userLastName: 'Doe',
};
// β
Good: no redundant prefixes in names
const user = {
firstName: 'John',
lastName: 'Doe',
};
Donβt use prefix/postfixβ
Do not use a prefix or postfix for names. Do not use value type prefixes (i.e. sName or aUserList). Do not use Hungarian notation for TypeScript types (i.e. IUser) or postfix with type name (i.e UserInterface).
// π« Bad: uses prefix or postfix
interface IUser {}
enum UserTypeEnum {}
// β
Good: no prefix or postfix
interface User {}
enum UserType {}
Postfix observables$β
To indicate that a property contains an observable stream, postfix it with a dollar sign (Finnish notation). This makes it easy to scan code for observables. Also, if you want to store the latest value in a property, you can use the same name without the dollar sign.
class UserDropdown {
#userConfig: User;
readonly userConfig$: Observable<UserConfig>;
public init() {
this.userConfig$.subscribe((userConfig) => (this.userConfig = userConfig));
}
}
In general, avoid situations where you manually subscribe to an observable. Read more about working with observables in the RxJS pages.
Casingβ
Use PascalCase for classes, interfaces, types, enums and genericsβ
We use PascalCase for classes, interfaces, types, enums and generics.
// Bad: illegal use of camelCase
class CAR_DEALER {}
enum carBrand {}
// Good: correct usage of PascalCase
class CarDealer {}
enum CarBrand {}
Use camelCase for functions, methods, properties, arguments and variablesβ
We use camelCase for functions, properties, arguments and variables.
// π« Bad: illegal usage of casing
class Car {
#FuelLevel: number;
add_fuel(Amount: number) {
const Current_level = this.#FuelLevel;
this.#FuelLevel = Current_level + Amount;
}
}
// β
Good: correct usage of casing
class Car {
#fuelLevel: number;
public addFuel(amount: number) {
const currentLevel = this.#fuelLevel;
this.#fuelLevel = currentLevel + amount;
// Or, even better of course:
this.#fuelLevel += amount;
}
}
Use SNAKE_UPPER_CASE for global constantsβ
We use SNAKE_UPPER_CASE for (global) constants.