Extending Built-In Models
You can customize and extend existing models in composable storefront to fit your requirements.
TypeScript gives developers a lot of confidence and safety, and speeds up development and library discovery. Where possible, composable storefront leverages the possibilities offered by TypeScript to provide a better developer experience. With composable storefront 2.1, you can take advantage of TypeScript's type augmentation capabilities.
Composable storefront has already typed most of the common objects that are used across the whole codebase, such as Cart and Product (and many more). However, the shape of these models was defined by composable storefront, which prevented you from adding properties to already-defined models. This could lead to difficulties working with the extra fields you may have needed in your customizations.
In future releases of composable storefront, more top-level exports will be added, which will allow you to augment them.
For more information about type augmentation in general, see Module Augmentation
in the TypeScript documentation.
Augmenting Modules
Now that the CostCenter can be augmented, you can alter its shape to fit your requirements. Let's say you need to display the originalCode field to users. Even if you have already adjusted the endpoint configuration and entity normalizers, TypeScript still does not automatically suggest that the originalCode key is also present on that model. To add it to the TypeScript type, you have to declare a new property on a CostCenter interface. The following is an example:
declare module '@spartacus/core' {
interface CostCenter {
originalCode?: string;
}
}
The module name @spartacus/core must be set according to the same value that you use to reference the type. In this case, the module is @spartacus/core, and you import the type as follows:
import { CostCenter } from '@spartacus/core';
From now on, when you work on an object of type CostCenter, the TypeScript compiler suggests the originalCode property in autocomplete, and allows you to define objects on this type normally, without having to hack the TypeScript with as CostCenter declarations.
Augmentation in Feature Libraries
You can also apply module augmentation techniques to feature libraries. To take an example from the composable storefront development team, we needed a CostCenter object for the @spartacus/organization library and the AdministrationCoreModule. However, we needed more properties than were defined in the @spartacus/core library.
Accordingly, we created a new cost-center.model.ts file where we could apply module augmentation. As with regular module augmentation, when augmenting a feature library, all properties should be optional. The following is an example:
import { Currency } from '@spartacus/core';
declare module '@spartacus/core' {
interface CostCenter {
activeFlag?: boolean;
currency?: Currency;
}
}
Next, we needed to reference this file. In the file that exposes all the organization models that are in the public API (feature-libs/organization/administration/core/model/index.ts), we added an import of this augmented model. The following is an example:
import './cost-center.model';
After that, anyone can safely use the new properties in the @spartacus/organization library, as well as in the app build that is based on this library. You can also augment the module in the app with your own properties. All these declarations are combined together, and in your application, you can use all the properties that are declared in @spartacus/core, @spartacus/organization, and in your module augmentation.
Augmenting Enums
All of the examples above describe how to augment interfaces, but you can augment enum as well. In most cases, you use const enum to augment enum values. The following is an example:
declare module '@spartacus/core' {
const enum ProductScope {
BULK_PRICING = 'bulkPricing',
}
}
The only times when you do not want to use const enum are when you are enumerating over enum values, or when you are dynamically assigning the enum value, such as when you map a property from a back end response to an enum value. In these cases, you augment enum instead of const enum.
When you augment const enum, the values are inlined during the TypeScript compilation, but when you augment enum, you need to explicitly provide a value for the underlying object, as well as the type. The following is an example:
declare module '@spartacus/core' {
enum ProductScope {
BULK_PRICING = 'bulkPricing',
}
}
(ProductScope as any)['BULK_PRICING'] = 'bulkPricing';