or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

advanced-schemas.mdcoercion.mdcollections.mderrors.mdindex.mdiso-datetime.mdjson-schema.mdlocales.mdnumber-formats.mdparsing.mdprimitives.mdrefinements.mdstring-formats.mdtransformations.mdunions-intersections.mdutilities.mdwrappers.md

transformations.mddocs/

0

# Transformations and Pipelines

1

2

Transform validated data or chain schemas together with type safety.

3

4

## Transform Method

5

6

```typescript { .api }

7

interface ZodType<Output, Input> {

8

transform<T>(fn: (val: Output) => T | Promise<T>): ZodEffects<this, T>;

9

}

10

```

11

12

**Examples:**

13

```typescript

14

// Basic transformation

15

z.string().transform((val) => val.toUpperCase())

16

z.string().transform((val) => val.length)

17

z.string().transform((val) => parseInt(val))

18

19

// Async transformation

20

z.string().transform(async (val) => {

21

const response = await fetch(`/api/${val}`);

22

return response.json();

23

})

24

// Requires: await schema.parseAsync(data);

25

26

// Transformation with validation

27

z.string()

28

.min(1)

29

.transform((val) => val.trim())

30

.transform((val) => val.toUpperCase())

31

32

// Object transformation

33

z.object({

34

firstName: z.string(),

35

lastName: z.string(),

36

}).transform((data) => ({

37

fullName: `${data.firstName} ${data.lastName}`,

38

}))

39

```

40

41

## Pipe

42

43

Chain schemas together: validate with first schema, then second.

44

45

```typescript { .api }

46

function pipe<A extends ZodTypeAny, B extends ZodTypeAny>(a: A, b: B): ZodPipeline<A, B>;

47

```

48

49

**Examples:**

50

```typescript

51

// String to number pipeline

52

z.pipe(z.string(), z.number())

53

// Parses "42" as string, then coerces to number

54

55

// Custom pipeline

56

const StringToDatePipeline = z.pipe(

57

z.string().regex(/^\d{4}-\d{2}-\d{2}$/),

58

z.string().transform((s) => new Date(s))

59

)

60

61

// Multi-stage pipeline

62

z.pipe(

63

z.string(),

64

z.string().transform((s) => s.trim()),

65

z.string().transform((s) => s.toLowerCase()),

66

z.string().email()

67

)

68

```

69

70

## Codec

71

72

Bidirectional encoding/decoding with custom logic.

73

74

```typescript { .api }

75

function codec<A extends ZodTypeAny, B extends ZodTypeAny = ZodTypeAny>(

76

in_: A,

77

out: B,

78

params: {

79

decode: (value: z.output<A>, payload: ParsePayload<z.output<A>>) => z.input<B> | Promise<z.input<B>>;

80

encode: (value: z.input<B>, payload: ParsePayload<z.input<B>>) => z.output<A> | Promise<z.output<A>>;

81

}

82

): ZodCodec<A, B>;

83

```

84

85

**Examples:**

86

```typescript

87

// Date codec

88

const DateCodec = z.codec(

89

z.string().transform((s) => new Date(s)),

90

z.date().transform((d) => d.toISOString())

91

);

92

93

DateCodec.decode("2024-01-01"); // Date object

94

DateCodec.encode(new Date()); // ISO string

95

96

// Custom codec

97

const Base64Codec = z.codec(

98

z.string(),

99

z.string(),

100

{

101

decode: (str) => Buffer.from(str, "base64").toString("utf8"),

102

encode: (str) => Buffer.from(str, "utf8").toString("base64"),

103

}

104

);

105

```

106

107

## Preprocess

108

109

Transform input before validation.

110

111

```typescript { .api }

112

function preprocess<T extends ZodTypeAny>(

113

preprocessor: (arg: unknown) => unknown,

114

schema: T

115

): ZodEffects<T>;

116

```

117

118

**Examples:**

119

```typescript

120

// Trim before validation

121

z.preprocess((val) => String(val).trim(), z.string().min(1))

122

123

// Coerce to number

124

z.preprocess((val) => Number(val), z.number())

125

126

// Parse JSON

127

z.preprocess(

128

(val) => typeof val === "string" ? JSON.parse(val) : val,

129

z.object({ name: z.string() })

130

)

131

132

// Form data preprocessing

133

z.preprocess(

134

(val) => val === "" ? undefined : val,

135

z.string().optional()

136

)

137

```

138

139

## Common Patterns

140

141

```typescript

142

// Parse and format dates

143

const DateSchema = z.string().transform((s) => new Date(s));

144

const FormattedDateSchema = z.date().transform((d) => d.toISOString());

145

146

// Parse JSON strings

147

const JSONSchema = z.string().transform((s) => JSON.parse(s));

148

const ParsedJSONSchema = z.preprocess(

149

(val) => typeof val === "string" ? JSON.parse(val) : val,

150

z.object({ data: z.any() })

151

);

152

153

// Normalize input

154

const NormalizedEmailSchema = z

155

.string()

156

.transform((s) => s.trim().toLowerCase())

157

.email();

158

159

// Chain transformations

160

const ProcessedSchema = z

161

.string()

162

.transform((s) => s.trim())

163

.transform((s) => s.toLowerCase())

164

.transform((s) => s.replace(/\s+/g, "-"))

165

.refine((s) => s.length > 0);

166

167

// Form data transformation

168

const FormSchema = z.object({

169

name: z.preprocess((val) => String(val).trim(), z.string()),

170

age: z.preprocess((val) => Number(val), z.number()),

171

active: z.preprocess((val) => val === "true", z.boolean()),

172

});

173

174

// Pipeline for multi-step validation

175

const UsernamePipeline = z.pipe(

176

z.string(),

177

z.string().min(3).max(20),

178

z.string().regex(/^[a-z0-9_]+$/),

179

z.string().transform((s) => s.toLowerCase())

180

);

181

```

182

183

## Type Inference

184

185

```typescript

186

const TransformSchema = z.string().transform((s) => s.length);

187

188

type Input = z.input<typeof TransformSchema>; // string

189

type Output = z.output<typeof TransformSchema>; // number

190

191

const PipelineSchema = z.pipe(z.string(), z.number());

192

type In = z.input<typeof PipelineSchema>; // string

193

type Out = z.output<typeof PipelineSchema>; // number

194

```

195