Skip to content

Commit

Permalink
feat: Adding JSDoc tag useStrict to output strict() modifier (#131)
Browse files Browse the repository at this point in the history
* feat: adding useStrict JSDoc tag to handle strict()

* feat: adding documentation

* fix: replacing @usestrict by @strict

---------

Co-authored-by: tvillaren <tvillaren@users.noreply.github.com>
  • Loading branch information
tvillaren and tvillaren committed May 17, 2023
1 parent 0db6307 commit f73a676
Show file tree
Hide file tree
Showing 3 changed files with 63 additions and 2 deletions.
13 changes: 11 additions & 2 deletions README.md
Expand Up @@ -29,7 +29,7 @@ Notes:
- Only exported types/interface are tested (so you can have some private types/interface and just exports the composed type)
- Even if this is not recommended, you can skip this validation step with `--skipValidation`. (At your own risk!)

## Validators
## JSDoc tags Validators

This tool supports some JSDoc tags inspired from openapi to generate zod validator.

Expand All @@ -44,7 +44,7 @@ List of supported keywords:
| `@format {"email"\|"uuid"\|"url"}` | `@format email` | `z.string().email()` |
| `@pattern {regex}` | `@pattern ^hello` | `z.string().regex(/^hello/)` |

Those JSDoc tags can also be combined:
Those validators can be combined:

```ts
// source.ts
Expand Down Expand Up @@ -128,6 +128,15 @@ export const heroContactSchema = z.object({
});
```

## Other JSDoc tags

Other JSDoc tags are available:

| JSDoc keyword | JSDoc Example | Description | Generated Zod |
| ------------------ | ------------- | ----------------------------------------- | ------------------------ |
| `@default {value}` | `@default 42` | Sets a default value for the property | `z.number().default(42)` |
| `@strict` | `@strict` | Adds the `strict()` modifier to an object | `z.object().strict()` |

## Advanced configuration

If you want to customized the schema name or restrict the exported schemas, you can do this by adding a `ts-to-zod.config.js` at the root of your project.
Expand Down
41 changes: 41 additions & 0 deletions src/core/generateZodSchema.test.ts
Expand Up @@ -847,6 +847,47 @@ describe("generateZodSchema", () => {
`);
});

it("should generate add strict() validation when @strict is used", () => {
const source = `/**
* @strict
*/
export type Superman = {
name: "superman";
weakness: Kryptonite;
age: number;
enemies: Array<string>;
};`;
expect(generate(source)).toMatchInlineSnapshot(`
"/**
* @strict
*/
export const supermanSchema = z.object({
name: z.literal(\\"superman\\"),
weakness: kryptoniteSchema,
age: z.number(),
enemies: z.array(z.string())
}).strict();"
`);
});

it("should generate add strict() validation when @strict is used on subtype", () => {
const source = `export interface A {
/** @strict */
a: {
b: number
}
}`;

expect(generate(source)).toMatchInlineSnapshot(`
"export const aSchema = z.object({
/** @strict */
a: z.object({
b: z.number()
}).strict()
});"
`);
});

it("should deal with nullable", () => {
const source = `export interface A {
/** @minimum 0 */
Expand Down
11 changes: 11 additions & 0 deletions src/core/jsDocTags.ts
Expand Up @@ -30,6 +30,7 @@ export interface JSDocTags {
maxLength?: TagWithError<number>;
format?: TagWithError<typeof formats[-1]>;
pattern?: string;
strict?: boolean;
}

/**
Expand Down Expand Up @@ -97,6 +98,13 @@ export function getJSDocTags(nodeType: ts.Node, sourceFile: ts.SourceFile) {
jsDoc.forEach((doc) => {
(doc.tags || []).forEach((tag) => {
const tagName = tag.tagName.escapedText.toString();

// Handling "unary operator" tag first (no tag.comment part needed)
if (tagName === "strict") {
jsDocTags[tagName] = true;
return;
}

if (!isJSDocTagKey(tagName) || typeof tag.comment !== "string") return;
const { value, errorMessage } = parseJsDocComment(tag.comment);

Expand Down Expand Up @@ -255,6 +263,9 @@ export function jsDocTagToZodProperties(
: [f.createStringLiteral(jsDocTags.default)],
});
}
if (jsDocTags.strict) {
zodProperties.push({ identifier: "strict" });
}

return zodProperties;
}
Expand Down

0 comments on commit f73a676

Please sign in to comment.