Skip to main content

TypeScript

Convex provides end-to-end type support when Convex functions are written in TypeScript.

You can gradually add TypeScript to a Convex project: the following steps provide progressively better type support. For the best support you'll want to complete them all.

Example: TypeScript and Schema

Writing Convex functions in TypeScript

The first step to improving type support in a Convex project is to writing your Convex functions in TypeScript by using the .ts extension.

If you are using argument validation, Convex will infer the types of your functions arguments automatically:

convex/sendMessage.ts
import { mutation } from "./_generated/server";
import { v } from "convex/values";

export default mutation({
args: {
body: v.string(),
author: v.string(),
},
// Convex knows that the argument type is `{body: string, author: string}`.
handler: async (ctx, args) => {
const { body, author } = args;
await ctx.db.insert("messages", { body, author });
},
});

Otherwise you can annotate the arguments type manually:

convex/sendMessage.ts
import { internalMutation } from "./_generated/server";

export default internalMutation({
// To convert this function from JavaScript to
// TypeScript you annotate the type of the arguments object.
handler: async (ctx, args: { body: string; author: string }) => {
const { body, author } = args;
await ctx.db.insert("messages", { body, author });
},
});

This can be useful for internal functions accepting complicated types.

If TypeScript is installed in your project npx convex dev and npx convex deploy will typecheck Convex functions before sending code to the Convex backend.

Convex functions are typechecked with the tsconfig.json in the Convex folder: you can modify some parts of this file to change typechecking settings, or delete this file to disable this typecheck.

You'll find most database methods have a return type of Promise<any> until you add a schema.

Adding a schema

Once you define a schema the type signature of database methods will be known. You'll also be able to use types imported from convex/_generated/dataModel in both Convex functions and clients written in TypeScript (React, React Native, Node.js etc.).

The types of documents in tables can be described using the Doc type from the generated data model and references to documents can be described with parametrized Document IDs.

src/App.tsx
import { Doc, Id } from "../convex/_generated/dataModel";

function Channel(props: { channelId: Id<"channels"> }) {}
function MessagesView(props: { message: Doc<"messages"> }) {}

Type annotating server-side helpers

When you want to reuse logic across Convex functions you'll want to define helper TypeScript functions, and these might need some of the provided context, to access the database, authentication and any other Convex feature.

Convex generates Context types based on your schema and declared Convex functions.

convex/helpers.ts
// Types based on your schema
import { Doc, Id } from "./_generated/dataModel";
// Types based on your schema and declared functions
import {
QueryCtx,
MutationCtx,
ActionCtx,
DatabaseReader,
DatabaseWriter,
} from "./_generated/server";
// Types that don't depend on schema or function
import {
Auth,
StorageReader,
StorageWriter,
StorageActionWriter,
} from "convex/server";

// Note that a `MutationCtx` also satisfies the `QueryCtx` interface
export function myReadHelper(ctx: QueryCtx, id: Id<"channels">) {
/* ... */
}

export function myActionHelper(ctx: ActionCtx, doc: Doc<"messages">) {
/* ... */
}

Writing frontend code in TypeScript

React hooks like useQuery and useMutation provide end to end type safety by ensuring their arguments match the signatures of the corresponding Convex functions. To use, install and configure TypeScript so you can write your React components in .tsx files instead of .jsx files.

Follow our React or Next.js quickstart to get started with Convex and TypeScript.

Convex works best with TypeScript version 5.0.3 or newer.