Skip to content

Improper Type Inference in Async Generator Callback #57903

Closed
@DScheglov

Description

@DScheglov

🔎 Search Terms

async generator inference, async generator

🕗 Version & Regression Information

  • This is a crash
  • This changed between versions 4.0.5 and 5.4.3
  • This changed in commit or PR _______
  • This is the behavior in every version I tried, and I reviewed the FAQ for entries about _________
  • I was unable to test this on prior versions because _______

⏯ Playground Link

https://www.typescriptlang.org/play?target=99&jsx=0&ts=5.4.3#code/JYOwLgpgTgZghgYwgAgEoQM4FcA2YA8AKgDTICiAfMgN4BQyyA2gMoCeAtgEYD2OAdMEhQ4YblAC6ACgCUALmQBxCCGgix+MqUIVaAX1q0wrAA4oActzDpseAPIwiVALzJCyCAA9IIACYY0mLgEcCCspCGsVAD8yCoAbtDI8oQA3IYmKGRQUIQZ9o7ILm6e3n4BNgRYIADWINwA7iCkoDCJlMgxZEmxEAlQaUamyLbVuab52oWu7l7KZdZB+C2JqKRVtQ0g0Wjd8dADGcgAghisIAgAUtycRKTtLjKFVCdnCEoqwqJQGloUabQ+CAIHBwKAoBDcEAYMDIOCnc4AEW48lu5AokgAVtd5C9zlcbiQ0dInsgAApQbjsYAYCD4BZ4fD0YajPIOSYAH2QFisgTsbIoxCZXU5WRyrMctAofwBQJBYOQEKhMPYcGMopRmmQADF0SrjKYoPJJNAKYbyMSnFQtRaqI5JGCKvJ6QRCZQbeVFoSdf9BigAELcbjVKbUZDAHzyaFQUAAcxSyDAghwEEjYGjIDjsKwYAAFmIAJIR5BR2PIXQHIZHbN5qAhsNFksZ+MgODsFPFtOl8vpIYBoMAdUEOarubEUz7wYAZDQs6OzSOa2X-oDgaDwZDochWmAEDmJ0bw6n0zH3eTKdTac78BPSAAiMy2QgAfS1tgAqmYEbepcvZWuFRuMLbruC5iAeDadhmp4UlSNJ0ryBCgVAd4Ps+r4fl+P4GJ4xhiDCiqbsBe6BtUg65khACMhRMpIPBBoWR6xjaTIMHCrxIpIbHnFuVQIImkIAFTII8dAMGJAFKsgdHVPIE5TKwwAQDgPhCXA9RwIIW4QDuxFBrRJGFtILHicgfC5sokh6qKkiPJayD3o+L7vp+T5+rYtgANK3rC-gEWA0hGcZDB+bONY4tWY4uApSkqbC6maURSH6UGfBwBFUCGUFYlmTmFlWdkNnug5aHOQiT5HG+hAABK2Kg3lwhJ0IBQYJnIGCYBYFAIAznwvXSeE6Vlj5yATmRw7pWk4m6NI-w4XhjVAdpu6jUOSEAEzUQwyXVAxHbHnIZIwRe8EVNeJFjUhKGOehLluZ53mcsVTkYeVlU1XVUpPMZXEIBxP08ec-EgEJIlZSF0mySR8mKcpqnxYtOkTttmWtQwOV5aq1m2VQT03WVd1ecNfnNWDgGhWBxyDVFMOxWpGkIyB6Xbal6Uo6j6MgJZmMFdj9moc9LkVdVtX1b5gEk617Wdd1oa9Xw-Xk7WujDSt5ETcZ01pEAA

💻 Code

interface Result<T, E> {
  [Symbol.iterator](): Generator<E, T>
}

type NotResultOf<T> = T extends Result<any, any> ? never : T;
type ErrTypeOf<T> = T extends Result<unknown, infer E> ? E : never;
type OkTypeOf<T> = T extends Result<infer R, unknown> ? R : never;
type AsyncJob<T, E> = () => AsyncGenerator<E, T>;

declare const asyncDo: <T, E>(job: AsyncJob<T, E>) => Promise<Result<
  OkTypeOf<T> | NotResultOf<T>,
  E | ErrTypeOf<T>
>>;
declare const mapErr: <E, F>(mapper: (error: E) => F) => <T>(result: Result<T, E>) => Result<T, F>;

type Book = { id: string; title: string; authorId: string };
type Author = { id: string; name: string };
type BookWithAuthor = Book & { author: Author };

declare const fetchBook: (id: string) => Promise<Result<Book, "NOT_FOUND">>;

declare const fetchAuthor: (id: string) => Promise<Result<Author, "NOT_FOUND">>;

export const fetchBookWithAuthor1 =
  (bookId: string) =>
    asyncDo(async function* () {
      const book: Book = yield* await fetchBook(bookId)
        .then(mapErr(() => "NOT_FOUND_BOOK" as const))

      const author: Author = yield* await fetchAuthor(book.authorId)
        .then(mapErr(() => "NOT_FOUND_AUTHOR" as const))

      return { ...book, author } as BookWithAuthor;
    });

export const fetchBookWithAuthor2 =
  (bookId: string): Promise<Result<BookWithAuthor, "NOT_FOUND_BOOK" | "NOT_FOUND_AUTHOR">> =>
    asyncDo(async function* () {
      const book: Book = yield* await fetchBook(bookId)
        .then(mapErr(() => "NOT_FOUND_BOOK" as const))

      const author: Author = yield* await fetchAuthor(book.authorId)
        .then(mapErr(() => "NOT_FOUND_AUTHOR" as const))

      return { ...book, author } as BookWithAuthor;
    });

🙁 Actual behavior

Compilation Error in the function fetchBookWithAuthor2

Type 'Author | BookWithAuthor' is not assignable to type 'Author'.
Property 'name' is missing in type 'BookWithAuthor' but required in type 'Author'.(2322)
input.ts(17, 29): 'name' is declared here.

🙂 Expected behavior

No errors.
compilation result must be the same as for fetchBookWithAuthor1.

The only difference between two versions of the function is a return type specified.

The defenition file is emitted correctly:

interface Result<T, E> {
    [Symbol.iterator](): Generator<E, T>;
}
type Book = {
    id: string;
    title: string;
    authorId: string;
};
type Author = {
    id: string;
    name: string;
};
type BookWithAuthor = Book & {
    author: Author;
};
export declare const fetchBookWithAuthor1: (bookId: string) => Promise<Result<BookWithAuthor, "NOT_FOUND_BOOK" | "NOT_FOUND_AUTHOR">>;
export declare const fetchBookWithAuthor2: (bookId: string) => Promise<Result<BookWithAuthor, "NOT_FOUND_BOOK" | "NOT_FOUND_AUTHOR">>;
export {};

Additional information about the issue

No response

Metadata

Metadata

Assignees

Labels

BugA bug in TypeScriptFix AvailableA PR has been opened for this issue

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions