Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add support for ISO 4217 Currencies #3881

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -798,6 +798,7 @@ z.string().date(); // ISO date format (YYYY-MM-DD)
z.string().time(); // ISO time format (HH:mm:ss[.SSSSSS])
z.string().duration(); // ISO 8601 duration
z.string().base64();
z.string().currency(); // ISO 4217 currencies
```

> Check out [validator.js](https://github.com/validatorjs/validator.js) for a bunch of other useful string validation functions that can be used in conjunction with [Refinements](#refine).
Expand Down
1 change: 1 addition & 0 deletions README_ZH.md
Original file line number Diff line number Diff line change
Expand Up @@ -492,6 +492,7 @@ z.string().ip(); // 默认为 IPv4 和 IPv6,选项见下文
z.string().trim(); // 减除空白
z.string().toLowerCase(); // 小写化
z.string().toUpperCase(); // 大写化
z.string().currency(); // ISO 4217
```

> 请查看 [validator.js](https://github.com/validatorjs/validator.js),了解可与 [Refinements](#refine) 结合使用的大量其他有用字符串验证函数。
Expand Down
1 change: 1 addition & 0 deletions deno/lib/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -798,6 +798,7 @@ z.string().date(); // ISO date format (YYYY-MM-DD)
z.string().time(); // ISO time format (HH:mm:ss[.SSSSSS])
z.string().duration(); // ISO 8601 duration
z.string().base64();
z.string().currency(); // ISO 4217 currencies
```

> Check out [validator.js](https://github.com/validatorjs/validator.js) for a bunch of other useful string validation functions that can be used in conjunction with [Refinements](#refine).
Expand Down
1 change: 1 addition & 0 deletions deno/lib/ZodError.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ export type StringValidation =
| "duration"
| "ip"
| "base64"
| "currency"
| { includes: string; position?: number }
| { startsWith: string }
| { endsWith: string };
Expand Down
39 changes: 39 additions & 0 deletions deno/lib/__tests__/string.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -374,6 +374,7 @@ test("checks getters", () => {
expect(z.string().email().isNANOID).toEqual(false);
expect(z.string().email().isIP).toEqual(false);
expect(z.string().email().isULID).toEqual(false);
expect(z.string().email().isCurrency).toEqual(false);

expect(z.string().url().isEmail).toEqual(false);
expect(z.string().url().isURL).toEqual(true);
Expand All @@ -383,6 +384,7 @@ test("checks getters", () => {
expect(z.string().url().isNANOID).toEqual(false);
expect(z.string().url().isIP).toEqual(false);
expect(z.string().url().isULID).toEqual(false);
expect(z.string().url().isCurrency).toEqual(false);

expect(z.string().cuid().isEmail).toEqual(false);
expect(z.string().cuid().isURL).toEqual(false);
Expand All @@ -392,6 +394,7 @@ test("checks getters", () => {
expect(z.string().cuid().isNANOID).toEqual(false);
expect(z.string().cuid().isIP).toEqual(false);
expect(z.string().cuid().isULID).toEqual(false);
expect(z.string().cuid().isCurrency).toEqual(false);

expect(z.string().cuid2().isEmail).toEqual(false);
expect(z.string().cuid2().isURL).toEqual(false);
Expand All @@ -401,6 +404,7 @@ test("checks getters", () => {
expect(z.string().cuid2().isNANOID).toEqual(false);
expect(z.string().cuid2().isIP).toEqual(false);
expect(z.string().cuid2().isULID).toEqual(false);
expect(z.string().cuid2().isCurrency).toEqual(false);

expect(z.string().uuid().isEmail).toEqual(false);
expect(z.string().uuid().isURL).toEqual(false);
Expand All @@ -410,6 +414,7 @@ test("checks getters", () => {
expect(z.string().uuid().isNANOID).toEqual(false);
expect(z.string().uuid().isIP).toEqual(false);
expect(z.string().uuid().isULID).toEqual(false);
expect(z.string().uuid().isCurrency).toEqual(false);

expect(z.string().nanoid().isEmail).toEqual(false);
expect(z.string().nanoid().isURL).toEqual(false);
Expand All @@ -419,6 +424,7 @@ test("checks getters", () => {
expect(z.string().nanoid().isNANOID).toEqual(true);
expect(z.string().nanoid().isIP).toEqual(false);
expect(z.string().nanoid().isULID).toEqual(false);
expect(z.string().nanoid().isCurrency).toEqual(false);

expect(z.string().ip().isEmail).toEqual(false);
expect(z.string().ip().isURL).toEqual(false);
Expand All @@ -428,6 +434,7 @@ test("checks getters", () => {
expect(z.string().ip().isNANOID).toEqual(false);
expect(z.string().ip().isIP).toEqual(true);
expect(z.string().ip().isULID).toEqual(false);
expect(z.string().ip().isCurrency).toEqual(false);

expect(z.string().ulid().isEmail).toEqual(false);
expect(z.string().ulid().isURL).toEqual(false);
Expand All @@ -437,6 +444,17 @@ test("checks getters", () => {
expect(z.string().ulid().isNANOID).toEqual(false);
expect(z.string().ulid().isIP).toEqual(false);
expect(z.string().ulid().isULID).toEqual(true);
expect(z.string().ulid().isCurrency).toEqual(false);

expect(z.string().currency().isEmail).toEqual(false);
expect(z.string().currency().isURL).toEqual(false);
expect(z.string().currency().isCUID).toEqual(false);
expect(z.string().currency().isCUID2).toEqual(false);
expect(z.string().currency().isUUID).toEqual(false);
expect(z.string().currency().isNANOID).toEqual(false);
expect(z.string().currency().isIP).toEqual(false);
expect(z.string().currency().isULID).toEqual(false);
expect(z.string().currency().isCurrency).toEqual(true);
});

test("min max getters", () => {
Expand Down Expand Up @@ -769,3 +787,24 @@ test("IP validation", () => {
invalidIPs.every((ip) => ipSchema.safeParse(ip).success === false)
).toBe(true);
});

test("currency", () => {
const currency = z.string().currency();
expect(currency.isCurrency).toEqual(true);

const validCurrencies = ["USD", "EUR", "CAD", "TOP", "ALL", "PEN"];

const invalidCurrencies = ["AAA", "322", "AXAXAX"];

const currencySchema = z.string().currency();
expect(
validCurrencies.every(
(currency) => currencySchema.safeParse(currency).success
)
).toBe(true);
expect(
invalidCurrencies.every(
(currency) => currencySchema.safeParse(currency).success === false
)
).toBe(true);
});
208 changes: 207 additions & 1 deletion deno/lib/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -565,7 +565,8 @@ export type ZodStringCheck =
}
| { kind: "duration"; message?: string }
| { kind: "ip"; version?: IpVersion; message?: string }
| { kind: "base64"; message?: string };
| { kind: "base64"; message?: string }
| { kind: "currency"; message?: string };

export interface ZodStringDef extends ZodTypeDef {
checks: ZodStringCheck[];
Expand Down Expand Up @@ -671,6 +672,193 @@ function isValidIP(ip: string, version?: IpVersion) {
return false;
}

// ISO 4217
const currencies = new Set([
"AED",
"AFN",
"ALL",
"AMD",
"ANG",
"AOA",
"ARS",
"AUD",
"AWG",
"AZN",
"BAM",
"BBD",
"BDT",
"BGN",
"BHD",
"BIF",
"BMD",
"BND",
"BOB",
"BOV",
"BRL",
"BSD",
"BTN",
"BWP",
"BYN",
"BZD",
"CAD",
"CDF",
"CHE",
"CHF",
"CHW",
"CLF",
"CLP",
"CNY",
"COP",
"COU",
"CRC",
"CUP",
"CVE",
"CZK",
"DJF",
"DKK",
"DOP",
"DZD",
"EGP",
"ERN",
"ETB",
"EUR",
"FJD",
"FKP",
"GBP",
"GEL",
"GHS",
"GIP",
"GMD",
"GNF",
"GTQ",
"GYD",
"HKD",
"HNL",
"HTG",
"HUF",
"IDR",
"ILS",
"INR",
"IQD",
"IRR",
"ISK",
"JMD",
"JOD",
"JPY",
"KES",
"KGS",
"KHR",
"KMF",
"KPW",
"KRW",
"KWD",
"KYD",
"KZT",
"LAK",
"LBP",
"LKR",
"LRD",
"LSL",
"LYD",
"MAD",
"MDL",
"MGA",
"MKD",
"MMK",
"MNT",
"MOP",
"MRU",
"MUR",
"MVR",
"MWK",
"MXN",
"MXV",
"MYR",
"MZN",
"NAD",
"NGN",
"NIO",
"NOK",
"NPR",
"NZD",
"OMR",
"PAB",
"PEN",
"PGK",
"PHP",
"PKR",
"PLN",
"PYG",
"QAR",
"RON",
"RSD",
"RUB",
"RWF",
"SAR",
"SBD",
"SCR",
"SDG",
"SEK",
"SGD",
"SHP",
"SLE",
"SOS",
"SRD",
"SSP",
"STN",
"SVC",
"SYP",
"SZL",
"THB",
"TJS",
"TMT",
"TND",
"TOP",
"TRY",
"TTD",
"TWD",
"TZS",
"UAH",
"UGX",
"USD",
"USN",
"UYI",
"UYU",
"UYW",
"UZS",
"VED",
"VES",
"VND",
"VUV",
"WST",
"XAF",
"XAG",
"XAU",
"XBA",
"XBB",
"XBC",
"XBD",
"XCD",
"XDR",
"XOF",
"XPD",
"XPF",
"XPT",
"XSU",
"XTS",
"XUA",
"XXX",
"YER",
"ZAR",
"ZMW",
"ZWL",
]);

function isValidCurrency(currency: string) {
if (currencies.has(currency)) return true;
return false;
}

export class ZodString extends ZodType<string, ZodStringDef, string> {
_parse(input: ParseInput): ParseReturnType<string> {
if (this._def.coerce) {
Expand Down Expand Up @@ -943,6 +1131,16 @@ export class ZodString extends ZodType<string, ZodStringDef, string> {
});
status.dirty();
}
} else if (check.kind === "currency") {
if (!isValidCurrency(input.data)) {
ctx = this._getOrReturnCtx(input, ctx);
addIssueToContext(ctx, {
validation: "currency",
code: ZodIssueCode.invalid_string,
message: check.message,
});
status.dirty();
}
} else {
util.assertNever(check);
}
Expand Down Expand Up @@ -1002,6 +1200,10 @@ export class ZodString extends ZodType<string, ZodStringDef, string> {
return this._addCheck({ kind: "base64", ...errorUtil.errToObj(message) });
}

currency(message?: errorUtil.ErrMessage) {
return this._addCheck({ kind: "currency", ...errorUtil.errToObj(message) });
}

ip(options?: string | { version?: IpVersion; message?: string }) {
return this._addCheck({ kind: "ip", ...errorUtil.errToObj(options) });
}
Expand Down Expand Up @@ -1203,6 +1405,10 @@ export class ZodString extends ZodType<string, ZodStringDef, string> {
return !!this._def.checks.find((ch) => ch.kind === "base64");
}

get isCurrency() {
return !!this._def.checks.find((ch) => ch.kind === "currency");
}

get minLength() {
let min: number | null = null;
for (const ch of this._def.checks) {
Expand Down
1 change: 1 addition & 0 deletions src/ZodError.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ export type StringValidation =
| "duration"
| "ip"
| "base64"
| "currency"
| { includes: string; position?: number }
| { startsWith: string }
| { endsWith: string };
Expand Down
Loading