Type Safety

Typeweaver's type safety comes from one place: the spec. The request schemas, response definitions, and operation metadata you write become the generated types used by handlers, validators, and clients.

The flow of types

In practice, the type flow looks like this:

  1. you define an operation in the spec
  2. Typeweaver generates request and response types from it
  3. server handlers receive typed request data
  4. clients send typed request commands and receive typed response unions

That means one contract drives both sides.

Example: the spec drives the request type

const GetTodoDefinition = defineOperation({
  operationId: "GetTodo",
  summary: "Get todo",
  method: HttpMethod.GET,
  path: "/todos/:todoId",
  request: {
    param: z.object({
      todoId: z.string(),
    }),
  },
  responses: [GetTodoSuccessDefinition, TodoNotFoundErrorDefinition],
});

From that, Typeweaver generates a request type and command that expect todoId.

await client.send(
  new GetTodoRequestCommand({
    param: {
      todoId: "todo_123",
    },
  })
);

If you rename todoId in the spec, TypeScript points to every client and handler still using the old name.

Example: the spec drives the response type

If GetTodo declares GetTodoSuccess and TodoNotFoundError, the generated client returns a union of exactly those responses.

const response = await client.send(new GetTodoRequestCommand({
  param: {
    todoId: "todo_123",
  },
}));

if (response.type === "GetTodoSuccess") {
  response.body.title;
} else {
  response.body.message;
}

Checking response.type narrows the union automatically, so you can work with the right body shape without guessing.

Type safety on the server side

Generated server integrations validate incoming requests before your handler runs. That means handler code works with request data that already matches the contract.

async handleGetTodoRequest(request) {
  return getTodoById(request.param.todoId);
}

You are not manually casting raw transport data.

Types and runtime validation work together

Static types help you while writing code. Validators help when real runtime data crosses the boundary.

You usually want both:

  • types to guide authoring and refactoring
  • validators to reject bad requests and bad responses at runtime

Type safety is strongest when the generated types and generated validators are both part of the flow.

Was this page helpful?