Rules Hub
Coding Rules Library
Align I/O type assertions with return generics
When parsing external data in generic functions, cast directly to the return type generic to avoid type mismatches or suppressed errors.
Bad example
| 1 | async function fetchApi<TOut, TIn = TOut>( |
| 2 | url: string, |
| 3 | transform?: (data: TIn) => TOut |
| 4 | ): Promise<TOut> { |
| 5 | const response = await fetch(url); |
| 6 | |
| 7 | // Bad: Casting to input type TIn causes errors when returning as TOut, |
| 8 | // often leading developers to use @ts-expect-error to silence it. |
| 9 | // @ts-expect-error |
| 10 | const data = (await response.json()) as TIn; |
| 11 |
|
| 12 | if (transform) return transform(data); |
| 13 | return data; // Error: TIn is not assignable to TOut |
| 14 | } |
Explanation (EN)
Casting the raw response to the input type (`TIn`) creates a type conflict when the function needs to return the output type (`TOut`) in the absence of a transformer. This forces developers to suppress errors, hiding potential issues.
Objašnjenje (HR)
Castanje sirovog odgovora u ulazni tip (`TIn`) stvara konflikt tipova kada funkcija mora vratiti izlazni tip (`TOut`) u slučaju da nema transformatora. To prisiljava programere na suzbijanje grešaka, što skriva potencijalne probleme.
Good example
| 1 | async function fetchApi<TOut, TIn = TOut>( |
| 2 | url: string, |
| 3 | transform?: (data: TIn) => TOut |
| 4 | ): Promise<TOut> { |
| 5 | const response = await fetch(url); |
| 6 | const rawData = await response.json(); |
| 7 |
|
| 8 | // Good: Cast explicitly to the return type TOut for the default case. |
| 9 | // This acknowledges that at the I/O boundary, we are asserting the shape. |
| 10 | const data = rawData as TOut; |
| 11 |
|
| 12 | if (transform) { |
| 13 | // Cast to TIn only specifically when passing to the transformer |
| 14 | return transform(rawData as TIn); |
| 15 | } |
| 16 | return data; |
| 17 | } |
Explanation (EN)
By casting the raw data directly to the return type (`TOut`), we satisfy the function's contract without type errors. We only cast to the input type (`TIn`) strictly when invoking the transform function, keeping assertions clean and logical.
Objašnjenje (HR)
Izravnim castanjem sirovih podataka u povratni tip (`TOut`) zadovoljavamo ugovor funkcije bez grešaka u tipovima. U ulazni tip (`TIn`) castamo samo onda kada pozivamo funkciju transformacije, čime održavamo asercije čistima i logičnima.