I created a type definition to ensure immutability of types and their properties all the way down. However, when I attempt to use this with a generic type, the compiler claims that the types do not overlap, preventing me from casting an object as immutable. My goal is to utilize my existing Immutable
definition with generics.
If I remove the generic or modify the immutable definition (by removing
as T[K] extends (...args: Array<any>) => any ? never
), then the casting works with generics. This seems to be related to that line.
The part
as T[K] extends (...args: Array<any>) => any ? never
is intended to exclude functions assigned to property K
in T
from being included in the immutability check, but there might be more to it since I didn't write the original code.
Below is the code snippet:
type Immutable<T> = T extends string | number | boolean | bigint | symbol | undefined | null
? Readonly<T>
: T extends object
? { readonly [K in keyof T as T[K] extends (...args: Array<any>) => any ? never : K]: Immutable<T[K]> }
: never;
interface IWithValue {
value: number;
}
interface IWithSelected<TData> {
selected: TData;
}
function f<T extends IWithValue>(
g: (x: Immutable<IWithSelected<T>>) => void,
x: IWithSelected<T>
) {
// Casting error due to incompatible types
g(x as Immutable<IWithSelected<T>>);
}
// Other function definitions...
Link to Playground for above code
Modifying the Immutable
definition eliminates the error:
type Immutable<T> = T extends string | number | boolean | bigint | symbol | undefined | null
? Readonly<T>
: T extends object
? { readonly [K in keyof T]: Immutable<T[K]> }
: never;
interface IWithValue {
value: number;
}
interface IWithSelected<TData> {
selected: TData;
}
function f<T extends IWithValue>(
g: (x: Immutable<IWithSelected<T>>) => void,
x: IWithSelected<T>
) {
// No errors here
g(x as Immutable<IWithSelected<T>>);
}