# Introduction This file describes the various style and design conventions that are used in the Backstage main repository. While you may choose to use these conventions in your own Backstage projects, it is not required. # TypeScript Coding Conventions Our TypeScript style is inspired by the [style guidelines](https://github.com/Microsoft/TypeScript/wiki/Coding-guidelines) of the TypeScript implementation itself. ## Naming 1. Use PascalCase for type names. 1. Do not use `I` as a prefix for interface names. 1. Use PascalCase for `enum` values. 1. Use `camelCase` for function names. 1. Use `camelCase` for property names and local variables. 1. Do not use `_` as a prefix for private properties. 1. Use whole words in names when possible. 1. Give type parameters names prefixed with `T`, for example `Request
`. ## Syntax and Types 1. Use `undefined`. Do not use `null`. 1. Prefer `for..of` over `.forEach`. 1. Do not introduce new types/values to the global namespace. ## File and Export Structure 1. Shared types should be defined in `types.ts`. 1. Keep `index.ts` free from implementation, it should only contain re-exports. 1. If a file has a single or a main export, name the file after the export. ## Error Handling 1. Rely on `@backstage/errors` for custom error types. ```ts throw new NotFoundError(`Could not find resource with id '${id}'`); ``` 1. Check error types by comparing the name ```ts if (error.name === 'NotFoundError') { // ... } ``` 1. Use `ResponseError` to convert `fetch` error responses. ```ts if (!res.ok) { throw await ResponseError.fromResponse(res); } ``` ## API Design This section describes guidelines for designing public APIs. It can also be applied to internal implementations, but it is less necessary. 1. Keep [SOLID](https://en.wikipedia.org/wiki/SOLID) principles in mind. 1. Be mindful of the number of top-level exports of each package, strive to keep it low. 1. Prioritize consistency over correctness, stick to existing patterns within the same class/file/folder/package. 1. Consume interfaces rather than concrete implementations. 1. Prefer classes for encapsulating functionality and implementing interfaces. 1. Suffix the name of concrete implementations with the name of the implemented interface. Use a prefix that describes the behavior of the implementation, or use a `Default` prefix. ```ts interface ImageLoader { ... } // interface for loading images class DefaultImageLoader implements ImageLoader { /* loads an image */ } class CachingImageLoader implements ImageLoader { /* caches loaded images */ } class ResizingImageLoader implements ImageLoader { /* resizes loaded images */ } ``` 1. Keep constructors private, prefer static factory methods for creating instances. ```ts class DefaultImageLoader implements ImageLoader { // Use `create` for the main way to create an instance. static create(options?: ImageLoaderOptions) { /* ... */ } // If there are multiple different types of instances that can be // created, suffix the create method. static createWithCaching(options?: ImageLoaderOptions) { /* ... */ } // If the instantiation process is based on a specific value, use `from*`. // The most common example of this is reading from configuration. // Use a second parameter in case additional options are needed. static fromConfig(config: Config, deps: { logger: Logger }) { /* ... */ } // Other types of values can work too static fromUrl(url: URL) { /* ... */ } // In order to make a private constructor available for testing you can use a // static factory marked as `@internal`, which will not show up in the public API. /** @internal */ static forTesting(internalOptions?: { ... }) { return new DefaultImageLoader(internalOptions); } private constructor(/* ... */) { /* ... */ } } ``` 1. Prefer common prefixes over suffixes when naming constants. ```ts // May be tempting to use `GITHUB_WIDGET_LABEL` instead. const WIDGET_LABEL_GITHUB = 'github'; const WIDGET_LABEL_GITLAB = 'gitlab'; const WIDGET_LABEL_BITBUCKET = 'bitbucket'; ``` 1. When a type relates directly to other symbols, use the name of those as prefix for the type. ```ts // Always prefix a prop type with the name of the component. function MyComponent(props: MyComponentProps) {} // Option types should be prefixed with the name of the operation. function upgradeWidget(options: UpgradeWidgetOptions) {} function activateWidget(options: ActivateWidgetOptions) {} // An exception to this are create methods, where the name of the thing // being created may be used as the prefix instead. function createWidget(options: WidgetOptions) {} // In this case the related names for request types are `ReportsApi` and // the method name. If there is a low risk of conflict we can keep them // short by only prefixing with the method name, but if there is a higher // risk of conflict then we would want to use the full prefix instead, while // omitting redundant parts, i.e. `ReportsApiUploadRequest. interface ReportsApi { uploadReports(request: UploadReportsRequest): Promise