Codemod to migrate from runtypes to zod
npm install runzodA codemod to migrate from runtypes to zod.
- Transforms imports/requires from runtypes to zod
- Converts type definitions to zod schemas
- Updates validation methods (check → parse, guard → safeParse)
- Converts Static
- Works with TypeScript files
- Preserves code style and formatting
``bash`
pnpm dlx runzod
`bash`
runzod
- --dry: Do not write to files, just show what would be changed--extensions
- : File extensions to process (default: ts,tsx)--help, -h
- : Show help message--verbose, -v
- : Show more information during processing
`bashTransform all TypeScript files in the src directory
pnpm dlx runzod ./src
Transformations
| Runtypes | Zod |
| ----------------------------------- | ----------------------------------- |
|
import { String } from 'runtypes' | import { z } from 'zod' |
| String | z.string() |
| Number | z.number() |
| Boolean | z.boolean() |
| BigInt | z.bigint() |
| Undefined | z.undefined() |
| Null | z.null() |
| Array(String) | z.array(z.string()) |
| Tuple(String, Number) | z.tuple([z.string(), z.number()]) |
| Object({...}) | z.object({...}) |
| Record(String, Number) | z.record(z.string(), z.number()) |
| Union(A, B, C) | z.union([A, B, C]) |
| Intersect(A, B) | z.intersection([A, B]) |
| Literal(x) | z.literal(x) |
| Optional(String) | z.string().optional() |
| String.optional() | z.string().optional() |
| String.withConstraint(...) | z.string().refine(...) |
| String.withBrand("Brand") | z.string().brand("Brand") |
| Type.check(data) | Type.parse(data) |
| Type.guard(data) | Type.safeParse(data) |
| Static | z.infer |Examples
$3
`typescript
// Before (runtypes)
import { String, Number, Boolean, Array, Object, type Static } from "runtypes";const User = Object({
name: String,
age: Number,
isActive: Boolean,
tags: Array(String),
});
type User = Static;
if (User.guard(data)) {
console.log(
User ${data.name} is ${data.age} years old);
}// After (zod)
import { z } from "zod";
const User = z.object({
name: z.string(),
age: z.number(),
isActive: z.boolean(),
tags: z.array(z.string()),
});
type User = z.infer;
const result = User.safeParse(data);
if (result.success) {
console.log(
User ${result.data.name} is ${result.data.age} years old);
}
`$3
`typescript
// Before (runtypes)
import { String, withBrand, type Static } from "runtypes";const UserId = String.withBrand("UserId");
type UserId = Static;
// After (zod)
import { z } from "zod";
const UserId = z.string().brand("UserId");
type UserId = z.infer;
`Limitations
The codemod handles most common cases, but there are some limitations:
- Complex constraints may need manual adjustment
- The
match pattern from runtypes needs manual conversion to zod patterns
- Recursive types may require adjustments
- Some method chaining might require manual fixes
- Branded type handling might require additional changesPost-Migration Steps
After running the codemod:
1. Add "zod" to your dependencies if it's not already there
2. Review the transformed files manually
3. Update validation logic based on zod's patterns:
-
runtype.guard(data) becomes schema.safeParse(data)
(but you'll need to access result.data when success is true)
- Error handling differs between libraries
4. Run your tests to ensure everything still worksDevelopment
`bash
Install dependencies
npm installBuild the project
npm run buildRun tests
npm testRun tests in watch mode
npm run test:watchTest on example files
npm run test:codemod
``MIT