redux-toolkit: Typescript: createAsyncThunk type infer not working correctly in 1.6.0 with multiple enum
In 1.5.1 work fine, in 1.6.0 not. If explicitly typed, everything ok.
Argument of type '() => Promise<Example>' is not assignable to parameter of type 'AsyncThunkPayloadCreator<Example.A, void, {}>'.
Type 'Promise<Example>' is not assignable to type 'MaybePromise<Example.A | RejectWithValue<unknown, unknown>>'.
Type 'Promise<Example>' is not assignable to type 'Promise<Example.A | RejectWithValue<unknown, unknown>>'.
Type 'Example' is not assignable to type 'Example.A | RejectWithValue<unknown, unknown>'.
Type 'Example.B' is not assignable to type 'Example.A | RejectWithValue<unknown, unknown>'.ts(2345)
export enum Example{
A = 0,
B = 1
}
export const bug = createAsyncThunk("foo/bar", async () => {
if (Math.random() > 0.5) {
return Example.A;
} else {
return Example.B;
}
});
About this issue
- Original URL
- State: closed
- Created 3 years ago
- Reactions: 2
- Comments: 15 (10 by maintainers)
Commits related to this issue
- fix #1156: union return values fall back to allowing only single member — committed to reduxjs/redux-toolkit by phryneas 3 years ago
- fix #1156: union return values fall back to allowing only single member (#1449) — committed to reduxjs/redux-toolkit by phryneas 3 years ago
Its acceptable to me, but i just pointed breaking change. It acceptable to you?
I have no idea what TypeScript is doing there, going from
boolean
to a strictfalse
- I can reproduce it. I’ll look into that.Meanwhile,
should do the trick.
@bfricka Dealing with types is, frankly, complex. It gets even more so when you as an end user are trying to wrap and reuse types from a library.
We do our best to avoid anything that might accidentally break user code, but honestly, sometimes these things happen. We can’t anticipate every way that users will use our APIs, or every possible interaction. That’s the nature of software development.
We have a ton of tests for both runtime and types behavior, and we put out several alphas and betas asking for feedback. No bugs were reported around this behavior.
What would help at the moment is reproductions that demonstrate code that worked in 1.5 and breaks in 1.6, so that we can look at those and try to figure out options for improving behavior (and add tests to ensure this doesn’t break again in the future). You’ve given a couple of those, and if you could provide any more as CodeSandboxes or repos (especially with some of these more complex use cases) that would be helpful.
Yes, it’s possible that the behaviour changed here slightly.
It should work if you add a return value like:
Does that sound like an acceptable solution for you?
TypeScript itself has breaking changes to all users of TypeScript in every minor version, similarly we also sometimes cannot avoid them on type level.
We are trying our best, but even though we have a full suite of type tests running against the last 5 versions of TS, things slip through.
Also, you might have noticed that we only do releases every few months, so if things slip through it might take some time to get back to it.
We are few people and doing this in our free time and there is much more to this project than just writing code - there is a lot of community work involved as well that is just as important.
Given that we are such a small team, real life has to have priority for us, as an Open Source project of this size otherwise can easily burn out the contributors. There have been enough examples of that over the last few years, so please keep that in mind and please in general keep in mind what message you are sending. This whole issue has not been very positive.
That said, I have looked into this and believe I have found a fix in #1449. Please try out the CodeSandbox CI build and report back.
Passing generic types into the function or having to define the return type is, to me, the crux of this issue and I consider 1.6 a breaking change for sure. Many of our AsyncThunks are now errors, where in 1.5 the correct return type was inferred.
Additionally, we use a factory to bind the state and some other types into
createAsyncThunk
and we define a default condition check. One of the points of this is that it gives us the types of ofState
andExtra
in our AsyncThunks without having to define any generic params. We instead only have to have a stable return type and define theThunkArg
. E.g.In this example, the return type is hinted to
http
generic.http
anddispatch
are fully typed because thiscreateAsyncThunk
is created from a factory w/ the types for those bound to our defaults. AndThunkArg
is obviously typed as you’d expect. This means that in callingconst a = await dispatch(fetchFoo({id: 'a'})).then(unwrapResult)
,a
isSomeReturn
and theThunkArg
is type checked.I’m seeing a bunch of issues with return types in 1.6 when nothing in our code has changed. For example, union types are failing and it’s showing the same sorts of errors as OP. Anything like:
Results in the error:
In this case, even giving the return type explicitly does nothing to fix the situation.
I’m happy with some of the improvements in 1.6, but this sort of breaking change should not have made it into a feature release.
1.6.1 broke one of my apps too. Type inference was not working anymore with an asyncthunk function returning a boolean