Type Safety in Spartacus
Note: Spartacus 4.x is no longer maintained. Please upgrade to the latest version.
Note: Spartacus 4.x was tested with SAP Commerce Cloud versions 1905 to 2205. Spartacus 4.x has not been verified to work with (and is not guaranteed to work with) SAP Commerce Cloud 2211 or later releases.
The entire Spartacus codebase has type safe methods, parameters, objects, and so on, with a few minor exceptions.
Use type safety everywhere that you can, including in all pull requests.
Note: When working with test files, it is important that *.spec.ts
files have type safe code that aligns with the files being tested.
If you are looking for generated OCC types, refer to projects/core/src/occ-models/occ.models.ts
.
Table of Contents
- Method Parameters and Return Values
- Defining Object Literals
- Working with Generics
- Exceptions
- Partial Usage for Service or Component Mocks
Method Parameters and Return Values
Although the void
return type is implicit in JavaScript and TypeScript, we prefer to make it explicit. The reasoning is that, if a method is modified in the future to return a value, then we would receive feedback from TypeScript to update the return type.
The following is an example of a method with an explicit void
return type:
addCartEntry(productCode: string, quantity: number): void {
...
}
Defining Object Literals
You can define an object literal with all properties, or with just a few, selected properties.
For example, a type-safe object literal can be defined as follows:
const userToken: UserToken = {
access_token: 'xxx',
token_type: 'bearer',
refresh_token: 'xxx',
expires_in: 1000,
scope: ['xxx'],
userId: 'xxx'
}
In this example, TypeScript is enforcing all the required properties of the UserToken
type to be defined.
However, in cases where only a few properties need to be defined (which is often true for tests), then the following syntax can be used:
const userToken = {
access_token: 'xxx'
} as UserToken;
Another way to define the same object literal is the following:
const userToken = <UserToken>{
access_token: 'xxx'
}
We encourage using the as
syntax, but the syntax using angle brackets <>
is also acceptable.
Working with Generics
There are some places in the code where generics are used, such as when working with CMS components.
Currently, there are no types for specific CMS components, but there is a generic CmsComponent
in cms-component.models.ts
. This component can be used as follows:
loadComponent<T extends CmsComponent>(
id: string,
pageContext: PageContext,
fields?: string
): Observable<T> {
const aComponent = <T>{...};
return of(aComponent);
}
For more information about advanced types in TypeSript, see the official TypeScript documentation.
Exceptions
Sometimes it is acceptable not to use type safety. For example, there are functions that can legitimately deal with any
type, such as interceptors.
The following is an example:
intercept(
request: HttpRequest<any>,
next: HttpHandler
): Observable<HttpEvent<any>> {
...
}
Partial Usage for Service or Component Mocks
When implementing mocks for services or components in unit tests, it is recommended to use Partial
with the mocked entity TypeScript type. The following is an example:
class MockUserIdService implements Partial<UserIdService> {
getUserId(): Observable<string> {
return of('');
}
clearUserId() {}
}
The benefits of this pattern are as follows:
- Mocked methods resemble the original methods much more, so tests are more accurate (for example, you do not forget to return a value)
- If the original methods are refactored, this refactoring is also propagated to the mocks in the unit test.