I try to wrap an AsyncThunk within an abstract crud thunk factory.
I simply want to define an interface for an AsyncThunk who needs at least an uuid as parameter.
export type IdentifiedElement = { uuid: string };
export interface CrudThunks<ElementType> {
getOneThunk: AsyncThunk<ElementType, IdentifiedElement, {}>;
}
Unfortunately, i can't assign to the getOneThunk an AsyncThunk defined with a params extending my IdentifiedElement :
interface GetOneParam extends IdentifiedElement {
anoterParam: string
}
const getOneThunk = createAsyncThunk<string, GetOneParam>(`/getOne`, async () => {
return "toto";
});
const myCruds: CrudThunks<string> = {
getOneThunk: getOneThunk
}
I got the following Error :
Type 'AsyncThunk<string, GetOneParam, {}>' is not assignable to type 'AsyncThunk<string, IdentifiedElement, {}>'.
Type 'AsyncThunk<string, GetOneParam, {}>' is not assignable to type '(arg: IdentifiedElement) => AsyncThunkAction<string, IdentifiedElement, {}>'.
Types of parameters 'arg' and 'arg' are incompatible.
Property 'anoterParam' is missing in type 'IdentifiedElement' but required in type 'GetOneParam'.
I was able to reproduce the bug with only some parts of my code in a TypeScript SandBox
This pattern works well with something else than AsyncThunk.
Did i miss something ? Is they're another way to achieve what i want to to ?
In your example, createAsyncThunk is expecting type GetOneParam. createAsyncThunk doesn't know anything about IdentifiedElement and can't "autocast" it. Also, second generic types of createAsyncThunk and AsyncThunk must match.
Replace IdentifiedElement with GetOneParam inside CrudThunks:
export interface CrudThunks<ElementType> {
getOneThunk: AsyncThunk<ElementType, GetOneParam, {}>;
}
I want to have a "generic/abstract" CrudThunks type that enforce the Thunk parameter to have at least an ID (to at least match the interface of IdentifiedElement. And in the same time have a specific Thunk with fully typed param.
I'll run on the CrudThunks some generic function which will only need an ID and do not care about the other attributes.
Typescript allow me to do the following without trouble, so i was expected to be able to do the same thing with an AsyncThunk.
const myGetOneParams: GetOneParam = {uuid: '01', anotherParam: 'query'};
let myIdentifiedElement: IdentifiedElement = myGetOneParams;
I they're a way i can type my AsyncCrud to achieve what i want to do ? Since, the same pattern works well with another generic type, i assume that i'm stuck by the type of AsyncThunk.
It looks like the AsyncThunk type check the assignation "both ways".
When we try to assign AsyncThunk<, GetOneParam,>to a AsyncThunk<, IdentifiedElement,> it evaluates the assignation of a GetOneParam to an IdentifiedElement (which should be ok) and then assign the IdentifiedElement to a GetOneParam (which logically fails).
Why the second check is needed since the right part of the affectation is a subtype of the left one ?
You are trying to assign type AsyncThunk<T, GetOneParam, {}> to type AsyncThunk<T, IdentifiedElement, {}>. These types are checked contravariantly by default. In your case, you can force typescript to check them bivariantly instead by specifying strictFunctionTypes: false in your tsconfig.json:
{
"compilerOptions": {
"strictFunctionTypes": false
}
}
More detailed explanation: https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-6.html#strict-function-types
Whoo ! thank you for the lecture, i'm pretty new to Typescript and i didn't know about this concept. It's make sense now, and i understand why it's could be dangerous.
I think i would not be able to achieve this abstraction without turning off this strict function type or ignore theses errors ...
Thank you for your time and your explanations !
Most helpful comment
You are trying to assign type
AsyncThunk<T, GetOneParam, {}>to typeAsyncThunk<T, IdentifiedElement, {}>. These types are checked contravariantly by default. In your case, you can force typescript to check them bivariantly instead by specifyingstrictFunctionTypes: falsein yourtsconfig.json:More detailed explanation: https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-6.html#strict-function-types