0
# Template Literals
1
2
Validates template literal strings with typed interpolation. Template runtypes allow validation of strings that follow specific patterns with variable parts validated by their own runtypes.
3
4
## Capabilities
5
6
### Template
7
8
Creates a validator for template literal strings with interpolated runtypes.
9
10
```typescript { .api }
11
/**
12
* Creates a validator for template literal strings with typed interpolation
13
* @param strings - String literal parts of the template
14
* @param runtypes - Runtypes for interpolated values
15
* @example Template(['Hello ', '!'], String).check("Hello World!") // "Hello World!"
16
* @example Template("user_", Number).check("user_123") // "user_123"
17
*/
18
function Template<A extends readonly [string, ...string[]], B extends readonly Runtype<LiteralValue>[]>(
19
strings: A,
20
...runtypes: B
21
): TemplateRuntype<A, B>;
22
23
// Alternative call signature for convenience
24
function Template<A extends readonly (LiteralValue | Runtype<LiteralValue>)[]>(
25
...args: A
26
): TemplateRuntype<ExtractStrings<A>, ExtractRuntypes<A>>;
27
28
interface TemplateRuntype<A, B> extends Runtype<TemplateType<A, B>> {
29
tag: "template";
30
strings: A;
31
runtypes: B;
32
}
33
34
type LiteralValue = undefined | null | boolean | number | bigint | string;
35
```
36
37
**Usage Examples:**
38
39
```typescript
40
import { Template, String, Number, Literal, Union } from "runtypes";
41
42
// Basic template validation
43
const Greeting = Template("Hello ", String, "!");
44
const greeting = Greeting.check("Hello World!"); // "Hello World!"
45
46
// ID patterns
47
const UserId = Template("user_", Number);
48
const PostId = Template("post_", String);
49
50
const userId = UserId.check("user_123"); // "user_123"
51
const postId = PostId.check("post_abc123"); // "post_abc123"
52
53
// Multiple interpolations
54
const LogEntry = Template("[", String, "] ", String, ": ", String);
55
const log = LogEntry.check("[INFO] 2024-01-01: Application started");
56
57
// Array-style syntax (for better type inference)
58
const VersionString = Template(["v", "."], Number, Number);
59
const version = VersionString.check("v1.2"); // "v1.2"
60
```
61
62
### URL and Path Patterns
63
64
```typescript
65
import { Template, String, Number, Union, Literal } from "runtypes";
66
67
// API endpoint patterns
68
const UserEndpoint = Template("/api/users/", Number);
69
const PostEndpoint = Template("/api/posts/", String, "/comments/", Number);
70
71
// Validate API paths
72
const userPath = UserEndpoint.check("/api/users/123");
73
const commentPath = PostEndpoint.check("/api/posts/my-post/comments/456");
74
75
// File path patterns
76
const ImagePath = Template("images/", String, ".", Union(Literal("jpg"), Literal("png"), Literal("gif")));
77
const imagePath = ImagePath.check("images/avatar.jpg");
78
79
// Query string patterns
80
const SearchQuery = Template("q=", String, "&page=", Number);
81
const query = SearchQuery.check("q=javascript&page=2");
82
```
83
84
### CSS and Styling Patterns
85
86
```typescript
87
import { Template, Number, String, Union, Literal } from "runtypes";
88
89
// CSS values
90
const PixelValue = Template(Number, "px");
91
const PercentValue = Template(Number, "%");
92
const EmValue = Template(Number, "em");
93
94
const CSSSize = Union(PixelValue, PercentValue, EmValue);
95
96
const width = PixelValue.check("100px"); // "100px"
97
const height = PercentValue.check("50%"); // "50%"
98
99
// Color patterns
100
const HexColor = Template("#", String); // Simplified - would need constraint for valid hex
101
const RgbColor = Template("rgb(", Number, ", ", Number, ", ", Number, ")");
102
103
const color1 = HexColor.check("#ff0000");
104
const color2 = RgbColor.check("rgb(255, 0, 0)");
105
106
// CSS class patterns
107
const BemClass = Template(String, "__", String, "--", String);
108
const bemClass = BemClass.check("button__text--highlighted");
109
```
110
111
### Date and Time Patterns
112
113
```typescript
114
import { Template, Number, String } from "runtypes";
115
116
// ISO date pattern (simplified)
117
const ISODate = Template(Number, "-", Number, "-", Number, "T", Number, ":", Number, ":", Number, "Z");
118
const timestamp = ISODate.check("2024-01-15T14:30:45Z");
119
120
// Custom date formats
121
const DateSlash = Template(Number, "/", Number, "/", Number);
122
const DateDash = Template(Number, "-", Number, "-", Number);
123
124
const date1 = DateSlash.check("12/31/2024");
125
const date2 = DateDash.check("2024-12-31");
126
127
// Time patterns
128
const TimeHM = Template(Number, ":", Number);
129
const TimeHMS = Template(Number, ":", Number, ":", Number);
130
131
const time1 = TimeHM.check("14:30");
132
const time2 = TimeHMS.check("14:30:45");
133
```
134
135
### Configuration and Environment Patterns
136
137
```typescript
138
import { Template, String, Number, Boolean } from "runtypes";
139
140
// Environment variable patterns
141
const DatabaseUrl = Template("postgresql://", String, ":", String, "@", String, ":", Number, "/", String);
142
const dbUrl = DatabaseUrl.check("postgresql://user:pass@localhost:5432/mydb");
143
144
// Configuration keys
145
const ConfigKey = Template("app.", String, ".", String);
146
const configKey = ConfigKey.check("app.database.host");
147
148
// Log levels with context
149
const LogLevel = Template("[", String, "] ", String);
150
const logEntry = LogLevel.check("[ERROR] Database connection failed");
151
```
152
153
## Advanced Template Patterns
154
155
### Nested Templates
156
157
```typescript
158
import { Template, String, Number, Union } from "runtypes";
159
160
// Combine templates for complex patterns
161
const Protocol = Union(Literal("http"), Literal("https"));
162
const Domain = Template(String, ".", String); // simplified domain pattern
163
const Url = Template(Protocol, "://", Domain, "/", String);
164
165
const url = Url.check("https://example.com/path");
166
167
// Reusable template components
168
const Prefix = Template("prefix_", String);
169
const Suffix = Template(String, "_suffix");
170
const Combined = Template(Prefix, "_", Suffix);
171
```
172
173
### Dynamic Validation with Templates
174
175
```typescript
176
import { Template, String, Number, Union, Literal } from "runtypes";
177
178
// API version patterns
179
const ApiV1 = Template("/api/v1/", String);
180
const ApiV2 = Template("/api/v2/", String);
181
const ApiPath = Union(ApiV1, ApiV2);
182
183
// Resource patterns with actions
184
const Action = Union(Literal("create"), Literal("read"), Literal("update"), Literal("delete"));
185
const ResourceAction = Template("/api/", String, "/", Action);
186
187
const resourcePath = ResourceAction.check("/api/users/create");
188
189
// Multi-tenant patterns
190
const TenantResource = Template("/tenant/", String, "/", String, "/", Number);
191
const tenantPath = TenantResource.check("/tenant/acme/users/123");
192
```
193
194
### Template with Constraints
195
196
```typescript
197
import { Template, String, Number, Constraint } from "runtypes";
198
199
// Email pattern (simplified)
200
const EmailLocal = String.withConstraint(s => s.length > 0 && !s.includes("@"));
201
const EmailDomain = String.withConstraint(s => s.includes("."));
202
const Email = Template(EmailLocal, "@", EmailDomain);
203
204
const email = Email.check("user@example.com");
205
206
// Phone number pattern
207
const AreaCode = Number.withConstraint(n => n >= 100 && n <= 999);
208
const PhoneNumber = Template("(", AreaCode, ") ", Number, "-", Number);
209
210
const phone = PhoneNumber.check("(555) 123-4567");
211
212
// Social security number pattern
213
const SSN = Template(Number, "-", Number, "-", Number);
214
const SsnWithConstraints = SSN.withConstraint(ssn => {
215
const parts = ssn.split("-");
216
return parts[0].length === 3 && parts[1].length === 2 && parts[2].length === 4;
217
});
218
```
219
220
### Template Parsing and Transformation
221
222
```typescript
223
import { Template, String, Number, Parser } from "runtypes";
224
225
// Parse template results into structured data
226
const VersionTemplate = Template("v", Number, ".", Number, ".", Number);
227
const VersionParser = VersionTemplate.withParser(versionString => {
228
const match = versionString.match(/v(\d+)\.(\d+)\.(\d+)/);
229
return {
230
major: parseInt(match![1]!),
231
minor: parseInt(match![2]!),
232
patch: parseInt(match![3]!)
233
};
234
});
235
236
const version = VersionParser.parse("v1.2.3");
237
// { major: 1, minor: 2, patch: 3 }
238
239
// Coordinate parsing
240
const CoordinateTemplate = Template("(", Number, ",", Number, ")");
241
const CoordinateParser = CoordinateTemplate.withParser(coordString => {
242
const match = coordString.match(/\(([^,]+),([^)]+)\)/);
243
return {
244
x: parseFloat(match![1]!),
245
y: parseFloat(match![2]!)
246
};
247
});
248
249
const coord = CoordinateParser.parse("(10.5,20.3)");
250
// { x: 10.5, y: 20.3 }
251
```
252
253
## Template Limitations and Workarounds
254
255
### Greedy Matching
256
257
```typescript
258
import { Template, String, Constraint } from "runtypes";
259
260
// Problem: String runtypes are greedy and match too much
261
// Template(String, String) creates pattern ^(.*)(.*)$ where first .* wins all
262
263
// Solution: Use constraints for specific patterns
264
const WordBoundary = String.withConstraint(s => /^\w+$/.test(s));
265
const TwoWords = Template(WordBoundary, " ", WordBoundary);
266
267
const twoWords = TwoWords.check("hello world");
268
269
// Or use more specific runtypes
270
const AlphaString = String.withConstraint(s => /^[a-zA-Z]+$/.test(s));
271
const NumericString = String.withConstraint(s => /^\d+$/.test(s));
272
const AlphaNumeric = Template(AlphaString, NumericString);
273
```
274
275
### Complex Patterns
276
277
```typescript
278
import { Constraint, String } from "runtypes";
279
280
// For very complex patterns, use single constraint instead of template
281
const ComplexPattern = String.withConstraint(s => {
282
// Custom validation logic for complex patterns
283
const regex = /^[A-Z]{2}\d{4}[A-Z]{2}$/;
284
return regex.test(s) || "Must match pattern: 2 letters, 4 digits, 2 letters";
285
});
286
287
const code = ComplexPattern.check("AB1234CD");
288
```