TypeScript Style Guide
Contents:
Observable Pipes
When piping on an observable:
Favor Isolated Pipe functions for multiple unrelated operations
Favor Empty Subscribe Functions
If there are multiple operations on an observable, favor moving that logic into pipe() functions:
// Do This
observable$
.pipe(
tap((x) => someFunc(x)),
tap((x) => someOtherFunc(x))
)
.subscribe();
// Instead of This
observable$.subscribe((x) => {
someFunc(x);
someOtherFunc(x);
});
Conventions in Classes
Favor Explicitly Public / Private Properties and Methods
Always include the public / private indication for properties and methods.
// do this
public someProp = '';
private _someOtherProp = '';
public someMethod(): string {}
// instead of this
someProp = '';
private _someOtherProp = '';
someMethod(): string {}
Favor Explicit Method Return Types
Unless a property / getter / setter is defined with a value literal, define the type:
// Do these
public someProperty: string | undefined;
public someMethod(): string {}
// instead of this
public someProperty;
public someMethod() {}
Avoid Definite Assignment Assertion for Properties
Except in rare circumstances, avoid the Definite Assignment Assertion for properties:
// Do this:
public someValue: string | undefined;
public someBool = false;
// Instead of this:
public someValue!: string;
public someBool!: boolean;
Avoid Using the "any" Type
The any type in typescript should be avoided in most cases. There are generally rare circumstances in which this should be used, as doing so essentially erases the benefits of using typescript over vanilla javascript.
That being said, there are notable exceptions to this rule:
"The any type is useful when you don’t want to write out a long type just to convince TypeScript that a particular line of code is okay." - TS Docs
"In some situations, not all type information is available or its declaration would take an inappropriate amount of effort. These may occur for values from code that has been written without TypeScript or a 3rd party library. In these cases, we might want to opt-out of type checking. To do so, we label these values with the any type" - Older TS Docs
Variable and Property Naming
Public Properties / Methods are lowerCamelCase
Private Properties are _lowerCamelCase With a Leading Underscore
private methods still follow the lowerCamelCase though:
RXJS
Function Notation in RxJS: Parentheses, Comma, and Bracket Notation
In RxJS/JavaScript/TypeScript development, the structure and format of our functions impacts readability, maintainability, and functionality. This section focuses on why using parentheses and comma notation in functions is not the best approach, and why bracket notation is the alternative our team favors in code reviews.
The Problem with Parentheses and Comma Notation
Consider the following example using parentheses and comma notation:
beforeEach(() => ((component = MockRender(MyComponent).point.componentInstance), (component.options = {})));
While this code is correct functionally, it introduces issues:
- Readability: The use of commas within parentheses to separate statements can lead to confusion, as it's not immediately clear that two separate assignments are taking place. This format can also be error-prone, especially when dealing with multiple operations. It's easy to overlook a comma or misinterpret the sequence of operations.
- Maintenance Challenges: For anyone reading or revising this code later, understanding and modifying the sequence of operations becomes more challenging, increasing the risk of introducing bugs during maintenance.
Advantages of Bracket Notation
beforeEach(() => {
component = MockRender(MyComponent).point.componentInstance;
component.options = {};
});
Using brackets ({}) around the function body offers several advantages:
- Clarity: Each line represents a distinct statement, making the flow of operations clearer and more straightforward.
- Scalability: This format is more scalable and adaptable to changes. Adding or removing statements or introducing new logic becomes simpler and less prone to errors.
- Debugging: Debugging is easier with bracket notation, as you can set breakpoints on individual statements and track the execution flow more effectively.
- Consistency: Bracket notation aligns with standard JavaScript practices for writing multi-statement functions, promoting consistency across your codebase.