While functional/prefer-immutable-types focuses on parameters, it is important to consider how parameters affect return types in pure functions.
If a function takes a Readonly<T> parameter and returns an object containing that parameter, the return type will be constrained by that immutability. To avoid unnecessarily constraining the return type of a function, use generics to capture the specific type of the input.
Recommended Pattern:
Instead of forcing a specific immutable type on the parameter, use a generic constraint to allow the caller to pass in their own type (whether mutable or immutable) while still ensuring the function treats it as immutable internally.
type Foo = { hello: number };
// ❌ Avoid: This forces the return type to always have an immutable 'foo'
function addBar(foo: Readonly<Foo>) {
return {
foo,
bar: { world: 2 },
};
}
// ✅ Better: Uses generics to preserve the caller's type information
function addBar<F extends Readonly<Foo>>(foo: F) {
return {
foo,
bar: { world: 2 },
};
}