Modern TypeScript for Medusa v2
Before writing code
Fetch live docs:
- Web-search
site:docs.medusajs.com typescript types modulesfor Medusa type patterns - Web-search
site:www.typescriptlang.org docs handbookfor latest TypeScript features - Web-search
site:docs.medusajs.com DML infer type modelfor DML type inference - Fetch
https://docs.medusajs.com/learn/fundamentals/modules/service-factoryfor service generics - Web-search
site:docs.medusajs.com container dependency injection resolvefor container typing
TypeScript Configuration in Medusa
Medusa v2 projects use strict TypeScript with specific compiler options:
| Option | Value | Purpose |
|---|---|---|
strict | true | Enable all strict type checks |
target | ES2021 | Modern JS output |
module | CommonJS | Module system |
moduleResolution | node | Node.js module resolution |
esModuleInterop | true | CJS/ESM interop |
skipLibCheck | true | Skip declaration file checks |
declaration | true | Generate .d.ts files |
outDir | .medusa/server | Build output directory |
DML Type Inference
InferTypeOf Pattern
Medusa generates TypeScript types from DML model definitions. Use InferTypeOf to derive entity types without manual interface duplication:
// Fetch live docs for InferTypeOf import path
// and exact DML-to-type mapping behavior
import { InferTypeOf } from "@medusajs/framework/types"
| DML Field | Inferred TypeScript Type |
|---|---|
.text() | string |
.number() | number |
.boolean() | boolean |
.dateTime() | Date |
.json() | Record<string, unknown> |
.enum(values) | Union literal type |
.id() | string |
.hasOne(Model) | Inferred model type |
.hasMany(Model) | Array of inferred model type |
.belongsTo(Model) | Inferred model type |
.nullable() | `T |
Service Generics
MedusaService Factory
The MedusaService factory generates a typed service class from DML models:
// Fetch live docs for MedusaService generic
// signature and generated method types
class MyService extends MedusaService({ MyModel }) {}
Generated Method Signatures
| Generated Method | Return Type | Purpose |
|---|---|---|
list(filters, config) | Promise<T[]> | List with filters and pagination |
retrieve(id, config) | Promise<T> | Get single entity by ID |
create(data) | Promise<T> | Create one entity |
update(data) | Promise<T> | Update entity by ID |
delete(id) | Promise<void> | Delete entity by ID |
listAndCount(filters, config) | Promise<[T[], number]> | List with total count |
softDelete(id) | Promise<T> | Soft delete entity |
restore(id) | Promise<T> | Restore soft-deleted entity |
Custom methods added to the service class are fully typed alongside generated ones.
Container Typing
Dependency Injection Container
Medusa resolves dependencies from a typed container. Module services are registered and resolved by key:
| Registration Pattern | Resolution Key | Type |
|---|---|---|
| Module service | ModuleRegistrationName.MODULE | Module service interface |
| Custom module service | Defined in module export | Custom service class |
| Remote query | ContainerRegistrationKeys.QUERY | RemoteQueryFunction |
| Logger | ContainerRegistrationKeys.LOGGER | Logger |
Typing the Container in Routes and Workflows
Access the container in API routes via req.scope.resolve("key"). In workflow steps, resolve via the StepFunction context. Use typed registration keys for compile-time safety.
Strict Mode Constraints
Key Strict Checks
| Check | Flag | Effect on Medusa Code |
|---|---|---|
strictNullChecks | Part of strict | Must handle null/undefined explicitly |
noImplicitAny | Part of strict | All parameters and variables must be typed |
strictPropertyInitialization | Part of strict | All class properties must be initialized |
noImplicitReturns | Recommended | All code paths must return a value |
noUnusedLocals | Recommended | Catch dead code early |
exactOptionalPropertyTypes | Optional | Distinguish undefined from missing |
Handling Nullable Types in Medusa
- DML
.nullable()fields produceT | null— always check before using retrieve()may throw if entity not found — wrap in try/catch or uselist()with filters- Optional config parameters use
?:— provide defaults where needed
Utility Types for Medusa
Commonly Used TypeScript Utilities
| Utility Type | Use Case in Medusa |
|---|---|
Partial<T> | Update DTOs (only changed fields required) |
Required<T> | Ensure all fields present in creation |
Pick<T, Keys> | Select specific fields for API responses |
Omit<T, Keys> | Exclude internal fields from public DTOs |
Record<string, T> | Metadata and JSON fields |
NonNullable<T> | Assert non-null after null check |
Awaited<T> | Unwrap Promise return types |
ReturnType<T> | Extract return type from functions |
Medusa-Specific Types
| Type | Import From | Purpose |
|---|---|---|
InferTypeOf | @medusajs/framework/types | Infer entity type from DML model |
MedusaRequest | @medusajs/framework/http | Typed HTTP request in API routes |
MedusaResponse | @medusajs/framework/http | Typed HTTP response in API routes |
StepResponse | @medusajs/framework/workflows-sdk | Typed workflow step return |
MedusaContainer | @medusajs/framework/types | DI container type |
Module Interface Patterns
Defining Module Types
Each custom module should export typed interfaces for its service:
// Fetch live docs for module type export
// conventions and service interface patterns
| File | Purpose |
|---|---|
src/modules/<name>/models/ | DML model definitions |
src/modules/<name>/service.ts | Service extending MedusaService |
src/modules/<name>/index.ts | Module definition with Module() factory |
src/modules/<name>/types.ts | Custom types and interfaces (optional) |
Generic Constraints on Module Boundaries
- Module services should accept and return typed DTOs, not raw entities
- Use
Pick/Omitto shape public API types from internal entity types - Define explicit input types for
createandupdateoperations
Zod Validators and Type Inference
API routes use Zod schemas for runtime validation. Infer TypeScript types from Zod schemas to avoid duplication:
// Fetch live docs for Zod schema usage in
// Medusa API route validators
import { z } from "zod"
| Pattern | Purpose |
|---|---|
z.infer<typeof schema> | Derive TS type from Zod schema |
defineMiddlewares | Register Zod validators on routes |
additionalDataValidator | Extend existing route validation |
Best Practices
- DML-first typing — define models in DML and use
InferTypeOfto derive types; never manually duplicate entity types as interfaces; keep the DML model as the single source of truth - Strict mode always — enable
strict: trueintsconfig.json; handle all nullable cases explicitly; useNonNullableand type guards instead of non-null assertions (!) - Container type safety — use typed registration keys when resolving from the container; avoid
anycasts on resolved services; define explicit return types on custom service methods - API type flow — define Zod schemas for request validation; infer TypeScript types from Zod with
z.infer; type API route handlers withMedusaRequest<T>andMedusaResponse
Fetch the Medusa TypeScript documentation for exact type imports, generic signatures, and DML inference patterns before implementing.