Rules Hub
Coding Rules Library
Rule priority, scope & exceptions
Use this to align rules with the senior-level structure (P0/P1/P2, scope, exceptions/tradeoffs).
Type fluent builder return values as `this`, not the class or interface name
Chainable methods should declare their return type as the polymorphic `this` type so chaining survives inheritance.
Bad example
| 1 | interface IDataProvider { |
| 2 | setLimit(limit: number): IDataProvider; |
| 3 | setOffset(offset: number): IDataProvider; |
| 4 | } |
| 5 |
|
| 6 | class BaseProvider implements IDataProvider { |
| 7 | setLimit(limit: number): IDataProvider { /* ... */ return this; } |
| 8 | setOffset(offset: number): IDataProvider { /* ... */ return this; } |
| 9 | } |
| 10 |
|
| 11 | class NewsProvider extends BaseProvider { |
| 12 | setCursor(cursor: string): this { /* ... */ return this; } |
| 13 | } |
| 14 |
|
| 15 | // Compile error: setLimit() is typed as IDataProvider, which has no setCursor |
| 16 | new NewsProvider().setLimit(20).setCursor('2024-01-15T10:30:00Z'); |
Explanation (EN)
Declaring the return as the base interface/class name erases the subtype. After calling an inherited method the static type collapses to the base, so subclass-specific methods are no longer chainable and the compiler rejects an otherwise valid chain.
Objašnjenje (HR)
Deklariranje povratnog tipa kao imena bazne sucelja/klase brise podtip. Nakon poziva naslijedene metode staticki tip se urusi na bazu, pa metode specificne za podklasu vise nisu ulancive i prevoditelj odbija inace valjani lanac.
Good example
| 1 | interface IDataProvider { |
| 2 | setLimit(limit: number): this; |
| 3 | setOffset(offset: number): this; |
| 4 | } |
| 5 |
|
| 6 | class BaseProvider implements IDataProvider { |
| 7 | setLimit(limit: number): this { /* ... */ return this; } |
| 8 | setOffset(offset: number): this { /* ... */ return this; } |
| 9 | } |
| 10 |
|
| 11 | class NewsProvider extends BaseProvider { |
| 12 | setCursor(cursor: string): this { /* ... */ return this; } |
| 13 | } |
| 14 |
|
| 15 | // Works: setLimit() returns `this` = NewsProvider, so setCursor is in scope |
| 16 | new NewsProvider().setLimit(20).setCursor('2024-01-15T10:30:00Z'); |
Explanation (EN)
`this` is the polymorphic this-type: it resolves to the concrete runtime class at each call site, so inherited chainable methods preserve the subtype. Subclasses gain working chains for free without redeclaring return types or adding generic parameters.
Objašnjenje (HR)
`this` je polimorfni this-tip: razrjesava se na konkretnu klasu izvodenja na svakom mjestu poziva, pa naslijedene ulancive metode cuvaju podtip. Podklase besplatno dobivaju ispravne lance bez ponovnog deklariranja povratnih tipova ili dodavanja generickih parametara.
Exceptions / Tradeoffs (EN)
If a method intentionally returns a different builder (a transition to another stage in a staged-builder API), keep that explicit type rather than `this`.
Iznimke / Tradeoffi (HR)
Ako metoda namjerno vraca drugaciji graditelj (prijelaz na drugu fazu u faznom builder API-ju), zadrzi taj eksplicitni tip umjesto `this`.