TypeScript Version: 2.4.1
Code
const promise = Promise.resolve(true);
const bar = () : Promise<string> => promise.then((condition) => {
if (condition) {
return 'foo';
}
return Promise.reject('bar');
});
tsc --target es6
Expected behavior:
Should compile.
Actual behavior:
src/foo.ts(3,50): error TS2345: Argument of type '(condition: boolean) => Promise<never> | "foo"' is not assignable to parameter of type '(value: boolean) => PromiseLike<never>'.
Type 'Promise<never> | "foo"' is not assignable to type 'PromiseLike<never>'.
Type '"foo"' is not assignable to type 'PromiseLike<never>'.
i guess this error because of the code const promise = Promise.resolve(true);. If you add type any to promise it will be compiled.
You shouldn't use Promise because Promise is a constructor;
this code will run successful:
const promise = Promise.resolve(true);
const bar = () : Promise<string> => promise.then((condition) => {
if (condition) {
return 'foo';
}
return new Promise((resolve, reject) => {
reject('bar');
});
});
@MrKou47 thanks for the response. You're right, that does make the code compile, but it seems like my original example should also work, since Promise.reject is a valid es6 method on the Promise constructor. In fact, your code is semantically identical iiuc.
// lib.es6.d.ts
PromiseConstructor {
// ... some code
/**
* Creates a new rejected promise for the provided reason.
* @param reason The reason the promise was rejected.
* @returns A new rejected Promise.
*/
reject(reason: any): Promise<never>;
// ...some code
resolve<T>(value: T | PromiseLike<T>): Promise<T>;
}
As you can see, Promise.reject(xxx) will return a Promise<never>. But the function bar which you declared want to return a Promise<string>.
And:
The never type is a subtype of, and assignable to, every type; however, no type is a subtype of, or assignable to, never (except never itself). Even any isn’t assignable to never.
If you use assert, The code will run successful too.
const promise = Promise.resolve(true);
const bar = () : Promise<string> => promise.then((condition) => {
if (condition) {
return 'foo';
}
return <Promise<string>>Promise.reject('bar');
});
I guess the answer is this
Thanks for your answer, that makes sense, but it seems like Promise<never> is not a correct or useful return type for Promise.reject.
Here, for what it's worth is what I came up with - you could definitely use this instead of the default one in lib.es6.d (or feel free to create a PR with it or a better version):
abstract class Promisish<T, E = Error> {
static reject<T = never, E = Error>(value: E): Promisish<T, E> {
return {} as Promisish<T, E>
}
static resolve<T, E = Error>(value: T): Promisish<T, E> {
return {} as Promisish<T, E>
}
abstract then<R, E2 = E>(f: (r: T) => R): Promisish<R, E>
abstract catch<R, E2 = E>(f: (e: E) => R): Promisish<T | R, E2>
abstract finally<E2 = E>(f: () => never): Promisish<T, E2>
}
Another instance of this bug (playground):
Promise.resolve(1).then(x => { // <-- Error here
if (Math.random() > 0.5) {
return Promise.resolve('foo');
} else {
return x;
}
});
Error: Argument of type '(x: number) => number | Promise<string>' is not assignable to parameter of type '(value: number) => string | PromiseLike<string>'.
Looks as if a mix of promise and non-promise return values confuse typescript (playground):
interface A {
isA: true;
}
interface B {
isA: false;
}
function x(): Promise<A | B> {
return Promise.resolve(true).then(wantA => { // <= Error here
if (wantA) {
return { isA: true as true};
}
return Promise.resolve().then(
() => ({ isA: false as false });
)
});
}
Argument of type '(wantA: boolean) => Promise<{ isA: false; }> | { isA: true; }' is not assignable to parameter of type '(value: boolean) => { isA: false; } | PromiseLike<{ isA: false; }>'.
Type 'Promise<{ isA: false; }> | { isA: true; }' is not assignable to type '{ isA: false; } | PromiseLike<{ isA: false; }>'.
Type '{ isA: true; }' is not assignable to type '{ isA: false; } | PromiseLike<{ isA: false; }>'.
Type '{ isA: true; }' is not assignable to type 'PromiseLike<{ isA: false; }>'.
Property 'then' is missing in type '{ isA: true; }'.
Edit: There is a simple way to calm typescript: Extract the body into a separate function and have it return the combined type A | B | Promise<A | B> (playground).
Seems to be fixed now
Most helpful comment
As you can see,
Promise.reject(xxx)will return aPromise<never>. But the functionbarwhich you declared want to return aPromise<string>.And:
If you use assert, The code will run successful too.
I guess the answer is this