-
Notifications
You must be signed in to change notification settings - Fork 13.1k
Closed
Labels
Fix AvailableA PR has been opened for this issueA PR has been opened for this issueNeeds InvestigationThis issue needs a team member to investigate its status.This issue needs a team member to investigate its status.
Milestone
Description
Bug Report
π Search Terms
promise, async, await, then, method, this, type
π Version & Regression Information
- This is the behavior in every version I tried, and I reviewed the FAQ for entries about Promises and async/await.
β― Playground Link
π» Code
type Either<E, A> = Left<E> | Right<A>;
type Left<E> = { tag: 'Left', e: E };
type Right<A> = { tag: 'Right', a: A };
const mkLeft = <E>(e: E): Either<E, never> => ({ tag: 'Left', e });
const mkRight = <A>(a: A): Either<never, A> => ({ tag: 'Right', a });
// I tried omitting the `implements` cause here but the behavior did not change.
class EPromise<E, A> implements PromiseLike<A> {
static succeed<A>(a: A): EPromise<never, A> {
return new EPromise(Promise.resolve(mkRight(a)));
}
static fail<E>(e: E): EPromise<E, never> {
return new EPromise(Promise.resolve(mkLeft(e)));
}
constructor(readonly p: PromiseLike<Either<E, A>>) { }
then<B = A, B1 = never>(
// EPromise can act as a Thenable only when `E` is `never`.
this: EPromise<never, A>,
onfulfilled?: ((value: A) => B | PromiseLike<B>) | null | undefined,
onrejected?: ((reason: any) => B1 | PromiseLike<B1>) | null | undefined
): PromiseLike<B | B1> {
return this.p.then(
// Casting to `Right<A>` is safe here because we've eliminated the possibility of `Left<E>`.
either => onfulfilled?.((either as Right<A>).a) ?? (either as Right<A>).a as unknown as B,
onrejected
)
}
}
const withTypedFailure: EPromise<number, string> = EPromise.fail(1);
// Errors as expected:
//
// "The 'this' context of type 'EPromise<number, string>' is not assignable to method's
// 'this' of type 'EPromise<never, string>"
withTypedFailure.then(s => s.toUpperCase()).then(console.log);
async function test() {
// Does not produce an equivalent error.
// We're attempting to access property `a` of a `Right<string>` but in reality we have a `Left<number>`,
// meaning that `str` here is `undefined`.
const str = await withTypedFailure;
// This will now cause a runtime error.
return str.toUpperCase();
}
test()
.then(console.log)
.catch(console.error) // [ERR]: Cannot read properties of undefined (reading 'toUpperCase')π Actual behavior
Awaiting a Thenable with a user-defined this type in the .then method does not respect the invariants imposed by the this type.
π Expected behavior
Awaiting a Thenable with a user-defined this type in the .then method should respect the invariants imposed by the this type and produce the same error as making an explicit call to .then.
Metadata
Metadata
Assignees
Labels
Fix AvailableA PR has been opened for this issueA PR has been opened for this issueNeeds InvestigationThis issue needs a team member to investigate its status.This issue needs a team member to investigate its status.