Skip to content

Commit

Permalink
fix record type with branded keys
Browse files Browse the repository at this point in the history
  • Loading branch information
netanelgilad committed Oct 18, 2024
1 parent 1f4f0da commit ef3f1ae
Show file tree
Hide file tree
Showing 5 changed files with 68 additions and 2 deletions.
4 changes: 4 additions & 0 deletions deno/lib/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -491,6 +491,7 @@ There are a growing number of tools that are built atop or support Zod natively!
- [`sveltekit-superforms`](https://github.com/ciscoheat/sveltekit-superforms): Supercharged form library for SvelteKit with Zod validation.
- [`mobx-zod-form`](https://github.com/MonoidDev/mobx-zod-form): Data-first form builder based on MobX & Zod.
- [`@vee-validate/zod`](https://github.com/logaretm/vee-validate/tree/main/packages/zod): Form library for Vue.js with Zod schema validation.
- [`zod-form-renderer`](https://github.com/thepeaklab/zod-form-renderer): Auto-infer form fields from zod schema and render them with react-hook-form with E2E type safety.

#### Zod to X

Expand All @@ -505,6 +506,7 @@ There are a growing number of tools that are built atop or support Zod natively!
- [`zod-openapi`](https://github.com/samchungy/zod-openapi): Create full OpenAPI v3.x documentation from Zod schemas.
- [`fastify-zod-openapi`](https://github.com/samchungy/fastify-zod-openapi): Fastify type provider, validation, serialization and @fastify/swagger support for Zod schemas.
- [`typeschema`](https://typeschema.com/): Universal adapter for schema validation.
- [`zodex`](https://github.com/commonbaseapp/zodex): (De)serialization for zod schemas

#### X to Zod

Expand Down Expand Up @@ -538,11 +540,13 @@ There are a growing number of tools that are built atop or support Zod natively!

- [`freerstore`](https://github.com/JacobWeisenburger/freerstore): Firestore cost optimizer.
- [`slonik`](https://github.com/gajus/slonik/tree/gajus/add-zod-validation-backwards-compatible#runtime-validation-and-static-type-inference): Node.js Postgres client with strong Zod integration.
- [`schemql`](https://github.com/a2lix/schemql): Enhances your SQL workflow by combining raw SQL with targeted type safety and schema validation.
- [`soly`](https://github.com/mdbetancourt/soly): Create CLI applications with zod.
- [`pastel`](https://github.com/vadimdemedes/pastel): Create CLI applications with react, zod, and ink.
- [`zod-xlsx`](https://github.com/sidwebworks/zod-xlsx): A xlsx based resource validator using Zod schemas.
- [`znv`](https://github.com/lostfictions/znv): Type-safe environment parsing and validation for Node.js with Zod schemas.
- [`zod-config`](https://github.com/alexmarqs/zod-config): Load configurations across multiple sources with flexible adapters, ensuring type safety with Zod.
- [`unplugin-environment`](https://github.com/r17x/js/tree/main/packages/unplugin-environment#readme): A plugin for loading enviroment variables safely with schema validation, simple with virtual module, type-safe with intellisense, and better DX 🔥 🚀 👷. Powered by Zod.

#### Utilities for Zod

Expand Down
24 changes: 24 additions & 0 deletions deno/lib/__tests__/record.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,20 @@ const recordWithLiteralKeys = z.record(
);
type recordWithLiteralKeys = z.infer<typeof recordWithLiteralKeys>;

const StringBrand = z.string().brand("StringBrand");
type StringBrand = z.infer<typeof StringBrand>;
const NumberBrand = z.number().brand("NumberBrand");
type NumberBrand = z.infer<typeof NumberBrand>;
const SymbolBrand = z.symbol().brand("SymbolBrand");
type SymbolBrand = z.infer<typeof SymbolBrand>;

const recordWithBrandedStringKeys = z.record(StringBrand, z.number());
type recordWithBrandedStringKeys = z.infer<typeof recordWithBrandedStringKeys>;
const recordWithBrandedNumberKeys = z.record(NumberBrand, z.number());
type recordWithBrandedNumberKeys = z.infer<typeof recordWithBrandedNumberKeys>;
const recordWithBrandedSymbolKeys = z.record(SymbolBrand, z.number());
type recordWithBrandedSymbolKeys = z.infer<typeof recordWithBrandedSymbolKeys>;

test("type inference", () => {
util.assertEqual<booleanRecord, Record<string, boolean>>(true);

Expand All @@ -29,6 +43,16 @@ test("type inference", () => {
recordWithLiteralKeys,
Partial<Record<"Tuna" | "Salmon", string>>
>(true);

util.assertEqual<recordWithBrandedStringKeys, Record<StringBrand, number>>(
true
);
util.assertEqual<recordWithBrandedNumberKeys, Record<NumberBrand, number>>(
true
);
util.assertEqual<recordWithBrandedSymbolKeys, Record<SymbolBrand, number>>(
true
);
});

test("methods", () => {
Expand Down
9 changes: 8 additions & 1 deletion deno/lib/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3531,7 +3531,11 @@ export type RecordType<K extends string | number | symbol, V> = [
? Record<K, V>
: [symbol] extends [K]
? Record<K, V>
: [BRAND<string | number | symbol>] extends [K]
: [string & AnyBrand] extends [K]
? Record<K, V>
: [number & AnyBrand] extends [K]
? Record<K, V>
: [symbol & AnyBrand] extends [K]
? Record<K, V>
: Partial<Record<K, V>>;
export class ZodRecord<
Expand Down Expand Up @@ -4904,6 +4908,9 @@ export const BRAND: unique symbol = Symbol("zod_brand");
export type BRAND<T extends string | number | symbol> = {
[BRAND]: { [k in T]: true };
};
export type AnyBrand = {
[BRAND]: any;
};

export class ZodBranded<
T extends ZodTypeAny,
Expand Down
24 changes: 24 additions & 0 deletions src/__tests__/record.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,20 @@ const recordWithLiteralKeys = z.record(
);
type recordWithLiteralKeys = z.infer<typeof recordWithLiteralKeys>;

const StringBrand = z.string().brand("StringBrand");
type StringBrand = z.infer<typeof StringBrand>;
const NumberBrand = z.number().brand("NumberBrand");
type NumberBrand = z.infer<typeof NumberBrand>;
const SymbolBrand = z.symbol().brand("SymbolBrand");
type SymbolBrand = z.infer<typeof SymbolBrand>;

const recordWithBrandedStringKeys = z.record(StringBrand, z.number());
type recordWithBrandedStringKeys = z.infer<typeof recordWithBrandedStringKeys>;
const recordWithBrandedNumberKeys = z.record(NumberBrand, z.number());
type recordWithBrandedNumberKeys = z.infer<typeof recordWithBrandedNumberKeys>;
const recordWithBrandedSymbolKeys = z.record(SymbolBrand, z.number());
type recordWithBrandedSymbolKeys = z.infer<typeof recordWithBrandedSymbolKeys>;

test("type inference", () => {
util.assertEqual<booleanRecord, Record<string, boolean>>(true);

Expand All @@ -28,6 +42,16 @@ test("type inference", () => {
recordWithLiteralKeys,
Partial<Record<"Tuna" | "Salmon", string>>
>(true);

util.assertEqual<recordWithBrandedStringKeys, Record<StringBrand, number>>(
true
);
util.assertEqual<recordWithBrandedNumberKeys, Record<NumberBrand, number>>(
true
);
util.assertEqual<recordWithBrandedSymbolKeys, Record<SymbolBrand, number>>(
true
);
});

test("methods", () => {
Expand Down
9 changes: 8 additions & 1 deletion src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3531,7 +3531,11 @@ export type RecordType<K extends string | number | symbol, V> = [
? Record<K, V>
: [symbol] extends [K]
? Record<K, V>
: [BRAND<string | number | symbol>] extends [K]
: [string & AnyBrand] extends [K]
? Record<K, V>
: [number & AnyBrand] extends [K]
? Record<K, V>
: [symbol & AnyBrand] extends [K]
? Record<K, V>
: Partial<Record<K, V>>;
export class ZodRecord<
Expand Down Expand Up @@ -4904,6 +4908,9 @@ export const BRAND: unique symbol = Symbol("zod_brand");
export type BRAND<T extends string | number | symbol> = {
[BRAND]: { [k in T]: true };
};
export type AnyBrand = {
[BRAND]: any;
};

export class ZodBranded<
T extends ZodTypeAny,
Expand Down

0 comments on commit ef3f1ae

Please sign in to comment.