Closed michaeljota closed 5 years ago
Just remove the await
from return await
. It causes the returned object literal to not be contextually typed.
Also note: you shouldn't use return await
unless you have one of the rare cases where it's really necessary
I mean that. As I said, I just wanted to recreate the escenario, but actually I'm not just return await. Just returing an object inside an promise, and then the type is lose.
@ajafff I update the issue with another reproducible scenario using Promise.resolve
.
We should unwrap a Promise through an await
expression to create a contextual type
Hey , can i work on this issue ?
Absolutely! I believe what you'll want to do is start at getContextualType
in checker.ts
. From there, you'll want to handle the case for AwaitExpression
. In that case I think you'll want to retrieve the contextual type from the AwaitExpression
itself. Let's call that T
. You'll want to contextually type it as T | PromiseLike<T>
Update here: I notice that in my case I was using an observable, and then returning it as a promise, but even in the Observable the literal type of type
was being loss.
I create an scenario that resemble more my use case, and may help to find the bug.
Also, this would not be directly about Promises anymore, but maybe how Typescript pass literal types as arguments to generic types.
@DanielRosenwasser @RyanCavanaugh
@DanielRosenwasser Does the type resolves correctly in the Observable scenario after the PR? I'm afraid I miss explain the problem.
If you notice here the type of the response
inside the tap
pipe function is string
, but should be One
.
Note you can fix the function type if you explicitly set the type of the return value, e.g.
const fn = () => 'one'; // return type: string
const fn = () => 'one' as 'one'; // return type: 'one'
function fn() {
const val = 'one';
return val; // return type: string
}
function fn() {
const val = 'one';
return val as typeof val; // return type: 'one'
}
but also, and this one is less obvious, the following:
function fn() {
const val: 'one' = 'one';
return val; // return type: 'one'
}
While I've run into this issue as well, I wonder whether it's a good idea to change the current behaviour. Most often, if a function returns a string literal, the developer wants the function's return type to be string
. The same goes for number literals that get widened to number
, boolean literals that get widened to boolean
etc.
This was fixed by #27270.
TypeScript Version: 3.1.0-dev.201xxxxx
Search Terms:
Code
This is a minimum reproducible code but I found this working with a complex scenario of observables and promises. However, the bug is still reproducible, because the type is being lose.
Expected behavior:
Inside the promise
type
type should be inferred as well.Actual behavior:
Throws because
string
is not assignable to neitherA
orB
.Playground Link:
[Link using async/await](http://www.typescriptlang.org/play/#src=interface%20ResponseA%20%7B%0D%0A%20%20%20%20type%3A%20'A'%2C%0D%0A%20%20%20%20payload%3A%20any%2C%0D%0A%7D%0D%0A%0D%0Ainterface%20ResponseB%20%7B%0D%0A%20%20%20%20type%3A%20'B'%2C%0D%0A%20%20%20%20payload%3A%20any%2C%0D%0A%7D%0D%0A%0D%0Atype%20ResponseKind%20%3D%20ResponseA%20%7C%20ResponseB%3B%0D%0A%0D%0A%2F%2F%20Does%20not%20work%20in%20strict%20mode%0D%0Aasync%20function%20returnResponseAsync()%3A%20Promise%3CResponseKind%3E%20%7B%0D%0A%20%20%20%20const%20type%20%3D%20'A'%3B%0D%0A%20%20%20%20const%20payload%20%3D%20%7B%7D%3B%0D%0A%0D%0A%20%20%20%20return%20await%20%7B%0D%0A%20%20%20%20%20%20%20%20type%2C%20%2F%2F%20type%20is%20'string'%20type%0D%0A%20%20%20%20%20%20%20%20payload%0D%0A%20%20%20%20%7D%0D%0A%7D%0D%0A%0D%0A%2F%2F%20Works%20in%20strict%20mode%0D%0Afunction%20returnResponse()%3A%20ResponseKind%20%7B%0D%0A%20%20%20%20const%20type%20%3D%20'A'%3B%0D%0A%20%20%20%20const%20payload%20%3D%20%7B%7D%3B%0D%0A%0D%0A%20%20%20%20return%20%7B%0D%0A%20%20%20%20%20%20%20%20type%2C%20%2F%2F%20type%20is%20'A'%20type%0D%0A%20%20%20%20%20%20%20%20payload%0D%0A%20%20%20%20%7D%0D%0A%7D)
[Link with Promise.resolve](http://www.typescriptlang.org/play/#src=interface%20ResponseA%20%7B%0D%0A%20%20%20%20type%3A%20'A'%2C%0D%0A%20%20%20%20payload%3A%20any%2C%0D%0A%7D%0D%0A%0D%0Ainterface%20ResponseB%20%7B%0D%0A%20%20%20%20type%3A%20'B'%2C%0D%0A%20%20%20%20payload%3A%20any%2C%0D%0A%7D%0D%0A%0D%0Atype%20ResponseKind%20%3D%20ResponseA%20%7C%20ResponseB%3B%0D%0A%0D%0A%2F%2F%20Does%20not%20work%20in%20strict%20mode%0D%0Afunction%20returnResponseAsync()%3A%20Promise%3CResponseKind%3E%20%7B%0D%0A%20%20%20%20const%20type%20%3D%20'A'%3B%0D%0A%20%20%20%20const%20payload%20%3D%20%7B%7D%3B%0D%0A%0D%0A%20%20%20%20return%20Promise.resolve(%7B%0D%0A%20%20%20%20%20%20%20%20type%2C%20%2F%2F%20type%20is%20'string'%20type%0D%0A%20%20%20%20%20%20%20%20payload%0D%0A%20%20%20%20%7D)%0D%0A%7D%0D%0A%0D%0A%2F%2F%20Works%20in%20strict%20mode%0D%0Afunction%20returnResponse()%3A%20ResponseKind%20%7B%0D%0A%20%20%20%20const%20type%20%3D%20'A'%3B%0D%0A%20%20%20%20const%20payload%20%3D%20%7B%7D%3B%0D%0A%0D%0A%20%20%20%20return%20%7B%0D%0A%20%20%20%20%20%20%20%20type%2C%20%2F%2F%20type%20is%20'A'%20type%0D%0A%20%20%20%20%20%20%20%20payload%0D%0A%20%20%20%20%7D%0D%0A%7D)
Link with Observable
Related Issues:
Workaround:
Just type the variable with a literal type.