Skip to content

Commit 98d10cb

Browse files
committed
Use definite assignment for required fields in docs and examples
1 parent 977f280 commit 98d10cb

11 files changed

Lines changed: 67 additions & 77 deletions

README.md

Lines changed: 32 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,12 @@ class Calculator extends Args {
2323
@argument({ description: "first number" })
2424
@type("number")
2525
@required()
26-
a?: number;
26+
a!: number;
2727

2828
@argument({ description: "second number" })
2929
@type("number")
3030
@required()
31-
b?: number;
31+
b!: number;
3232

3333
@description("operation to perform")
3434
operation = "add";
@@ -37,12 +37,8 @@ class Calculator extends Args {
3737
// Parse command line arguments
3838
const args = Calculator.parse(["10", "5"]);
3939

40-
// Handle potentially undefined values
41-
if (args.a !== undefined && args.b !== undefined) {
42-
console.log(`${args.a} ${args.operation} ${args.b} = ${args.a + args.b}`);
43-
} else {
44-
console.error("Both numbers are required");
45-
}
40+
// Use the required values directly
41+
console.log(`${args.a} ${args.operation} ${args.b} = ${args.a + args.b}`);
4642
```
4743

4844
Usage:
@@ -137,7 +133,7 @@ if (args.serve) {
137133
```typescript ignore
138134
@type("string")
139135
@required()
140-
apiKey?: string;
136+
apiKey!: string;
141137
```
142138

143139
**Optional properties** (with default values):
@@ -164,7 +160,7 @@ apiKey?: string;
164160
// ✅ Fixed - add @type decorator
165161
@type("string")
166162
@required()
167-
apiKey?: string;
163+
apiKey!: string;
168164

169165
// ✅ Or provide a default (no @type needed)
170166
apiKey = "";
@@ -193,13 +189,13 @@ class DeployCommand extends Args {
193189
@argument({ description: "Application name to deploy" })
194190
@type("string")
195191
@required()
196-
appName?: string;
192+
appName!: string;
197193

198194
@argument({ description: "Target environment" })
199195
@type("string")
200196
@addValidator(oneOf(["dev", "staging", "prod"]))
201197
@required()
202-
environment?: string;
198+
environment!: string;
203199

204200
@argument({ description: "Version to deploy (optional)" })
205201
@type("string")
@@ -218,33 +214,28 @@ class DeployCommand extends Args {
218214
@description("API key for authentication")
219215
@type("string")
220216
@required()
221-
apiKey?: string;
217+
apiKey!: string;
222218
}
223219

224220
// Parse arguments
225221
const args = DeployCommand.parse(["myapp", "prod", "--apiKey", "secret123"]);
226222

227-
// Type-safe usage with proper undefined checking
228-
if (args.appName && args.environment && args.apiKey) {
229-
console.log(`Deploying ${args.appName} to ${args.environment}`);
230-
231-
if (args.version) {
232-
console.log(`Version: ${args.version}`);
233-
} else {
234-
console.log("Version: latest");
235-
}
236-
237-
if (args.verbose) {
238-
console.log(`Instances: ${args.instances}`);
239-
console.log(`Force mode: ${args.force}`);
240-
console.log(`API Key: ${args.apiKey.substring(0, 4)}...`);
241-
}
223+
// Type-safe usage - required fields are guaranteed
224+
console.log(`Deploying ${args.appName} to ${args.environment}`);
242225

243-
// Proceed with deployment...
226+
if (args.version) {
227+
console.log(`Version: ${args.version}`);
244228
} else {
245-
console.error("Missing required arguments");
246-
Deno.exit(1);
229+
console.log("Version: latest");
230+
}
231+
232+
if (args.verbose) {
233+
console.log(`Instances: ${args.instances}`);
234+
console.log(`Force mode: ${args.force}`);
235+
console.log(`API Key: ${args.apiKey.substring(0, 4)}...`);
247236
}
237+
238+
// Proceed with deployment...
248239
```
249240

250241
Usage examples:
@@ -294,16 +285,12 @@ class Server extends Args {
294285
@description("API key for authentication")
295286
@type("string")
296287
@required()
297-
apiKey?: string;
288+
apiKey!: string;
298289
}
299290

300291
const config = Server.parse(["--apiKey", "secret123"]);
301-
if (config.apiKey) {
302-
console.log(`Starting server on ${config.host}:${config.port}`);
303-
console.log(`API Key: ${config.apiKey.substring(0, 4)}...`);
304-
} else {
305-
console.error("API key is required");
306-
}
292+
console.log(`Starting server on ${config.host}:${config.port}`);
293+
console.log(`API Key: ${config.apiKey.substring(0, 4)}...`);
307294
```
308295

309296
### Application with Subcommands
@@ -391,7 +378,7 @@ class FileProcessor extends Args {
391378
@argument({ description: "Input file to process" })
392379
@type("string")
393380
@required()
394-
input?: string;
381+
input!: string;
395382

396383
@argument({ description: "Output file" })
397384
@type("string")
@@ -415,13 +402,11 @@ const args = FileProcessor.parse([
415402
"transform",
416403
]);
417404

418-
// Handle potentially undefined values
419-
if (args.input) {
420-
console.log(`Processing ${args.input} -> ${args.output || "stdout"}`);
421-
console.log(`Mode: ${args.mode}`);
422-
if (args.extras && args.extras.length > 0) {
423-
console.log(`Extras: ${args.extras.join(", ")}`);
424-
}
405+
// Required field is guaranteed to be present
406+
console.log(`Processing ${args.input} -> ${args.output || "stdout"}`);
407+
console.log(`Mode: ${args.mode}`);
408+
if (args.extras && args.extras.length > 0) {
409+
console.log(`Extras: ${args.extras.join(", ")}`);
425410
}
426411
```
427412

@@ -525,7 +510,7 @@ class User extends Args {
525510
@type("string")
526511
@required()
527512
@email()
528-
email?: string;
513+
email!: string;
529514
}
530515
```
531516

examples/cli_api_example.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,12 @@ class Calculator extends Args {
2525
@description("First number to operate on")
2626
@type("number")
2727
@required()
28-
a?: number;
28+
a!: number;
2929

3030
@description("Second number to operate on")
3131
@type("number")
3232
@required()
33-
b?: number;
33+
b!: number;
3434

3535
@description("Operation to perform")
3636
@addValidator(oneOf(["add", "subtract", "multiply", "divide"]))

examples/error-handling.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ class StartCommand {
117117
@required()
118118
@type("string")
119119
@description("Configuration file path")
120-
config: string = "";
120+
config!: string;
121121
}
122122

123123
@command
@@ -303,7 +303,7 @@ class ConfigManager {
303303
@description("Database URL")
304304
@required()
305305
@type("string")
306-
dbUrl: string = "";
306+
dbUrl!: string;
307307

308308
@description("API port")
309309
port: number = 3000;

examples/example.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -69,11 +69,11 @@ class ServeCommand {
6969
class BuildCommand {
7070
@description("Output directory")
7171
@required()
72-
output: string = "";
72+
output!: string;
7373

7474
@description("Source files to build")
7575
@required()
76-
sources: string[] = [];
76+
sources!: string[];
7777

7878
@description("Enable minification")
7979
minify: boolean = false;
@@ -108,7 +108,7 @@ class ProcessCommand {
108108
@argument({ description: "Input file to process" })
109109
@required()
110110
@type("string")
111-
input: string = "";
111+
input!: string;
112112

113113
@argument({ description: "Output file path" })
114114
@type("string")

mod.ts

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,21 +12,19 @@
1212
* @description("First number")
1313
* @type("number")
1414
* @required()
15-
* a?: number;
15+
* a!: number;
1616
*
1717
* @description("Second number")
1818
* @type("number")
1919
* @required()
20-
* b?: number;
20+
* b!: number;
2121
*
2222
* @description("Operation to perform")
2323
* operation = "add";
2424
* }
2525
*
2626
* const args = Calculator.parse(["--a", "10", "--b", "5"]);
27-
* if (args.a !== undefined && args.b !== undefined) {
28-
* console.log(`${args.a} ${args.operation} ${args.b} = ${args.a + args.b}`);
29-
* }
27+
* console.log(`${args.a} ${args.operation} ${args.b} = ${args.a + args.b}`);
3028
* ```
3129
*
3230
* @example With subcommands

src/decorators.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ export interface DecoratorContext {
5757
* // Explicit type for property without default
5858
* @type("number")
5959
* @required()
60-
* timeout?: number;
60+
* timeout!: number;
6161
*
6262
* // Override default-inferred type
6363
* @type("string[]")
@@ -116,7 +116,7 @@ export function type(
116116
* @argument({ description: "Input file to process" })
117117
* @type("string")
118118
* @required()
119-
* input?: string;
119+
* input!: string;
120120
* }
121121
* ```
122122
*/
@@ -233,7 +233,7 @@ export function addValidator(validator: Validator): (
233233
* // Required with explicit type
234234
* @type("string")
235235
* @required()
236-
* apiKey?: string;
236+
* apiKey!: string;
237237
*
238238
* // Required with default (makes the default required if not overridden)
239239
* @required()
@@ -281,7 +281,7 @@ export function required(): (
281281
* @validate((value: string) => value.length >= 8, "must be at least 8 characters")
282282
* @type("string")
283283
* @required()
284-
* password?: string;
284+
* password!: string;
285285
* }
286286
* ```
287287
*/
@@ -483,7 +483,7 @@ export function subCommand<T extends new () => unknown>(
483483
* @argument({ description: "Input file to process" })
484484
* @type("string")
485485
* @required()
486-
* input?: string;
486+
* input!: string;
487487
*
488488
* // Optional second argument
489489
* @argument({ description: "Output file" })
@@ -565,7 +565,7 @@ export function argument(
565565
* @argument({ description: "Binary name to execute" })
566566
* @type("string")
567567
* @required()
568-
* binary?: string;
568+
* binary!: string;
569569
*
570570
* @rawRest("Arguments to pass to the binary")
571571
* @type("string[]")

src/metadata.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ function isUserDefinedProperty(descriptor: PropertyDescriptor): boolean {
7676
* @argument({ description: "Input file" })
7777
* @type("string")
7878
* @required()
79-
* input?: string;
79+
* input!: string;
8080
*
8181
* @argument({ description: "Output file" })
8282
* @type("string")
@@ -264,7 +264,7 @@ function validatePositionalArguments(
264264
* // From explicit @type() decorator
265265
* @type("number")
266266
* @required()
267-
* timeout?: number; // → "number"
267+
* timeout!: number; // → "number"
268268
*
269269
* // From default value
270270
* port = 3000; // → "number"

src/validation.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ export function validateValue(
6565
* @type("string")
6666
* @addValidator(requiredValidator())
6767
* @required()
68-
* name?: string;
68+
* name!: string;
6969
* }
7070
* ```
7171
*/

tests/cli_api.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ Deno.test("Args API - required fields", () => {
9494
@description("Required API key")
9595
@type("string")
9696
@required()
97-
apiKey: string = "";
97+
apiKey!: string;
9898

9999
@description("Optional debug flag")
100100
debug: boolean = false;

0 commit comments

Comments
 (0)