or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

index.mdpattern-matching.mdpatterns.mdvalidation.md

validation.mddocs/

0

# Type Validation

1

2

Runtime type validation and type guard generation using pattern matching. Perfect for validating API responses, user input, and unknown data structures.

3

4

## Capabilities

5

6

### Type Guard Generation

7

8

Creates type guard functions from patterns for runtime type checking with compile-time type safety.

9

10

```typescript { .api }

11

/**

12

* Creates a type guard function from a pattern

13

* @param pattern - Pattern to match against

14

* @returns Type guard function that narrows unknown to matched type

15

*/

16

function isMatching<const p extends Pattern<unknown>>(

17

pattern: p

18

): (value: unknown) => value is P.infer<p>;

19

```

20

21

**Usage Examples:**

22

23

```typescript

24

import { isMatching, P } from "ts-pattern";

25

26

// Create type guards

27

const isUser = isMatching({

28

id: P.number,

29

name: P.string,

30

email: P.string.regex(/\S+@\S+\.\S+/)

31

});

32

33

const isApiResponse = isMatching({

34

success: P.boolean,

35

data: P.optional(P.any),

36

error: P.optional(P.string)

37

});

38

39

// Use type guards

40

function processUnknownData(data: unknown) {

41

if (isUser(data)) {

42

// data is now typed as { id: number; name: string; email: string }

43

console.log(`User: ${data.name} (${data.email})`);

44

return data.id;

45

}

46

47

if (isApiResponse(data)) {

48

// data is now typed as the API response interface

49

if (data.success && data.data) {

50

return data.data;

51

} else {

52

throw new Error(data.error || 'Unknown API error');

53

}

54

}

55

56

throw new Error('Invalid data format');

57

}

58

```

59

60

### Direct Pattern Validation

61

62

Validates if a value matches a pattern directly without creating a type guard function.

63

64

```typescript { .api }

65

/**

66

* Validates if a value matches a pattern

67

* @param pattern - Pattern to match against

68

* @param value - Value to validate

69

* @returns Boolean indicating if value matches pattern

70

*/

71

function isMatching<const T, const P extends Pattern<T>>(

72

pattern: P,

73

value: T

74

): value is T & P.infer<P>;

75

```

76

77

**Usage Examples:**

78

79

```typescript

80

// Direct validation

81

const data = { name: "Alice", age: 25, active: true };

82

83

if (isMatching({ name: P.string, age: P.number.gte(18) }, data)) {

84

// data is validated and typed properly

85

console.log(`Adult user: ${data.name}, age ${data.age}`);

86

}

87

88

// Validate API responses

89

async function fetchUserData(userId: number) {

90

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

91

const data = await response.json();

92

93

if (isMatching({

94

id: P.number,

95

name: P.string,

96

profile: P.optional({

97

avatar: P.string.startsWith('http'),

98

bio: P.string

99

})

100

}, data)) {

101

return data; // properly typed user data

102

}

103

104

throw new Error('Invalid user data format');

105

}

106

```

107

108

### Complex Validation Patterns

109

110

Advanced patterns for validating complex data structures.

111

112

```typescript { .api }

113

// Pattern constraint type for additional properties

114

type PatternConstraint<T> = T extends readonly any[]

115

? P.Pattern<T>

116

: T extends object

117

? P.Pattern<T> & UnknownProperties

118

: P.Pattern<T>;

119

120

// Unknown properties interface for object patterns

121

interface UnknownProperties {

122

[key: string]: unknown;

123

}

124

```

125

126

**Usage Examples:**

127

128

```typescript

129

// Validate nested structures

130

const isNestedConfig = isMatching({

131

database: {

132

host: P.string,

133

port: P.number.between(1, 65535),

134

credentials: P.union(

135

{ type: 'password', username: P.string, password: P.string },

136

{ type: 'token', token: P.string }

137

)

138

},

139

features: P.array(P.string),

140

debug: P.boolean.optional()

141

});

142

143

// Validate arrays with specific patterns

144

const isUserList = isMatching(P.array({

145

id: P.number,

146

name: P.string.minLength(1),

147

roles: P.array(P.union('admin', 'user', 'guest')),

148

lastLogin: P.union(P.instanceOf(Date), P.nullish)

149

}));

150

151

// Validate with custom predicates

152

const isValidEventPayload = isMatching({

153

type: P.string,

154

timestamp: P.when(ts => ts instanceof Date && ts <= new Date()),

155

payload: P.shape(P.any), // allows any object structure

156

metadata: P.optional(P.any)

157

});

158

159

// Use in validation functions

160

function validateUserInput(input: unknown) {

161

if (!isNestedConfig(input)) {

162

throw new Error('Invalid configuration format');

163

}

164

165

// input is now properly typed

166

const { database, features, debug = false } = input;

167

168

if (database.credentials.type === 'password') {

169

// TypeScript knows this is password credentials

170

return connectWithPassword(database.host, database.port,

171

database.credentials.username,

172

database.credentials.password);

173

} else {

174

// TypeScript knows this is token credentials

175

return connectWithToken(database.host, database.port,

176

database.credentials.token);

177

}

178

}

179

```

180

181

### Validation with Error Handling

182

183

Combining validation with proper error handling and user feedback.

184

185

**Usage Examples:**

186

187

```typescript

188

// Validation with detailed error messages

189

function validateAndProcessData<T>(

190

data: unknown,

191

pattern: Pattern<T>,

192

errorMessage: string

193

): T {

194

if (isMatching(pattern, data)) {

195

return data;

196

}

197

198

throw new Error(`${errorMessage}. Received: ${JSON.stringify(data)}`);

199

}

200

201

// Form validation

202

interface UserForm {

203

email: string;

204

password: string;

205

age: number;

206

terms: boolean;

207

}

208

209

const userFormPattern = {

210

email: P.string.regex(/^[^\s@]+@[^\s@]+\.[^\s@]+$/),

211

password: P.string.minLength(8).regex(/(?=.*[a-z])(?=.*[A-Z])(?=.*\d)/),

212

age: P.number.int().between(13, 120),

213

terms: P.when((x: boolean) => x === true)

214

};

215

216

function validateUserForm(formData: unknown): UserForm {

217

try {

218

return validateAndProcessData(

219

formData,

220

userFormPattern,

221

'Invalid user form data'

222

);

223

} catch (error) {

224

// Add specific validation error details

225

if (typeof formData === 'object' && formData !== null) {

226

const data = formData as Record<string, unknown>;

227

228

if (!isMatching(P.string.regex(/^[^\s@]+@[^\s@]+\.[^\s@]+$/), data.email)) {

229

throw new Error('Invalid email format');

230

}

231

232

if (!isMatching(P.string.minLength(8), data.password)) {

233

throw new Error('Password must be at least 8 characters');

234

}

235

236

if (!isMatching(P.number.int().between(13, 120), data.age)) {

237

throw new Error('Age must be between 13 and 120');

238

}

239

240

if (data.terms !== true) {

241

throw new Error('Terms and conditions must be accepted');

242

}

243

}

244

245

throw error;

246

}

247

}

248

249

// API response validation

250

async function safeApiCall<T>(

251

url: string,

252

responsePattern: Pattern<T>

253

): Promise<T> {

254

try {

255

const response = await fetch(url);

256

257

if (!response.ok) {

258

throw new Error(`HTTP ${response.status}: ${response.statusText}`);

259

}

260

261

const data = await response.json();

262

263

if (isMatching(responsePattern, data)) {

264

return data;

265

}

266

267

throw new Error(`Invalid API response format from ${url}`);

268

} catch (error) {

269

console.error('API call failed:', error);

270

throw error;

271

}

272

}

273

274

// Usage

275

const userData = await safeApiCall('/api/user/123', {

276

id: P.number,

277

name: P.string,

278

email: P.string,

279

preferences: P.optional({

280

theme: P.union('light', 'dark'),

281

notifications: P.boolean

282

})

283

});

284

```