elysiajs / elysia

Ergonomic Framework for Humans
https://elysiajs.com
MIT License
10.63k stars 226 forks source link

Mapped types are not computed #880

Closed klydra closed 1 month ago

klydra commented 1 month ago

What version of Elysia is running?

1.1.20

What platform is your computer?

Independent

What steps can reproduce the bug?

Generate a type using the .map operator.

  export const elysia = new Elysia().post("/test", ({ body }) => body, {
    body: Type.Object({
      test: Type.Union((["test"] as const).map((item) => Type.Literal(item))),
    }),
  });

What is the expected behavior?

The type should compute the literal(s) and resolve.

What do you see instead?

The type is not computed while being computed in the frontend layer causing type incompatibility.

Additional information

No response

Have you try removing the node_modules and bun.lockb and try again yet?

No response

deadlinecode commented 1 month ago

Mapping it like this won't work because the order of the array needs to be non-mutable You can use this magic function to get a literal type from a readonly array uwu

const LiteralUnion = <T extends Array<any>>(...args: T) =>
  Type.Union(args.map((x) => Type.Literal(x))) as TUnion<{
    [K in keyof T]: TLiteral<T[K]>;
  }>;

// IMPORTANT the array needs to be readonly/non-mutable
const roles = ["asdf", "test", 2] as const;

const role = LiteralUnion(...roles);

const app = new Elysia()
  .post("/createUser", () => "Dummes Beispiel ich weis", {
    body: t.Object({
      role,
    }),
  })
  .listen(3000);

type App = typeof app;

const client = treaty<App>("");

client.createUser.post({
  // Type Error: Type '""' is not assignable to type '"asdf" | "test" | 2'. 
  role: "",
});

This is btw not a issue with elysia or typebox but rather how typescript works :3