One of the utilities in my library is a type similar to the following:
type Action<Model extends object> = (data: State<Model>) => State<Model>;
This utility type allows the declaration of an "action" function that operates against a generic Model
.
The argument data
in the "action" function is typed using another utility type exported:
type State<Model extends object> = Omit<Model, KeysOfType<Model, Action<any>>>;
The State
utility type takes the input Model
and creates a new type by removing all properties of type Action
.
For example, here is a basic user implementation:
interface MyModel {
counter: number;
increment: Action<Model>;
}
const myModel = {
counter: 0,
increment: (data) => {
data.counter; // Typed as `number`
data.increment; // Does not exist
return data;
}
}
However, I am facing an issue when defining a generic model along with a factory function to create instances of the model.
For instance:
interface MyModel<T> {
value: T;
doSomething: Action<MyModel<T>>;
}
function modelFactory<T>(value: T): MyModel<T> {
return {
value,
doSomething: data => {
data.value; // Doesn't exist
data.doSomething; // Exists
return data;
}
};
}
In this example, I expect the data
argument to still have the generic value
property even after the doSomething
action has been removed. However, this is not the case.
I believe this is due to the generic T
intersecting with the Action
type and getting removed from the argument type.
Is there a workaround for this limitation in TypeScript?
You can access the full code snippet for debugging here: https://codesandbox.io/s/reverent-star-m4sdb?fontsize=14