Skip to content

Commit

Permalink
feat: copy code from PR #6720 to merge PRs
Browse files Browse the repository at this point in the history
Co-authored-by: Tobi <tzdesign@users.noreply.github.com>
  • Loading branch information
fabian-hiller and tzdesign committed Aug 2, 2024
1 parent 4222770 commit 6f63fee
Show file tree
Hide file tree
Showing 5 changed files with 124 additions and 19 deletions.
2 changes: 1 addition & 1 deletion packages/docs/src/routes/api/qwik-city/api.json
Original file line number Diff line number Diff line change
Expand Up @@ -922,7 +922,7 @@
}
],
"kind": "TypeAlias",
"content": "```typescript\nexport type ValidatorErrorKeyDotNotation<T, Prefix extends string = ''> = IsAny<T> extends true ? never : T extends object ? {\n [K in keyof T & string]: T[K] extends (infer U)[] ? U extends object ? `${Prefix}${K}[]` | `${Prefix}${K}[]${ValidatorErrorKeyDotNotation<U, '.'>}` : `${Prefix}${K}[]` : T[K] extends object ? ValidatorErrorKeyDotNotation<T[K], `${Prefix}${K}.`> : `${Prefix}${K}`;\n}[keyof T & string] : never;\n```\n**References:** [ValidatorErrorKeyDotNotation](#validatorerrorkeydotnotation)",
"content": "```typescript\nexport type ValidatorErrorKeyDotNotation<T, Prefix extends string = ''> = IsAny<T> extends true ? never : T extends object ? {\n [K in keyof T & string]: IsAny<T[K]> extends true ? never : T[K] extends (infer U)[] ? IsAny<U> extends true ? never : U extends object ? `${Prefix}${K}[]` | ValidatorErrorKeyDotNotation<U, `${Prefix}${K}[].`> : `${Prefix}${K}[]` : T[K] extends object ? ValidatorErrorKeyDotNotation<T[K], `${Prefix}${K}.`> : `${Prefix}${K}`;\n}[keyof T & string] : never;\n```\n**References:** [ValidatorErrorKeyDotNotation](#validatorerrorkeydotnotation)",
"editUrl": "https://github.com/QwikDev/qwik/tree/main/packages/qwik-city/src/runtime/src/types.ts",
"mdFile": "qwik-city.validatorerrorkeydotnotation.md"
},
Expand Down
22 changes: 13 additions & 9 deletions packages/docs/src/routes/api/qwik-city/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -2443,15 +2443,19 @@ export type ValidatorErrorKeyDotNotation<T, Prefix extends string = ""> =
? never
: T extends object
? {
[K in keyof T & string]: T[K] extends (infer U)[]
? U extends object
?
| `${Prefix}${K}[]`
| `${Prefix}${K}[]${ValidatorErrorKeyDotNotation<U, ".">}`
: `${Prefix}${K}[]`
: T[K] extends object
? ValidatorErrorKeyDotNotation<T[K], `${Prefix}${K}.`>
: `${Prefix}${K}`;
[K in keyof T & string]: IsAny<T[K]> extends true
? never
: T[K] extends (infer U)[]
? IsAny<U> extends true
? never
: U extends object
?
| `${Prefix}${K}[]`
| ValidatorErrorKeyDotNotation<U, `${Prefix}${K}[].`>
: `${Prefix}${K}[]`
: T[K] extends object
? ValidatorErrorKeyDotNotation<T[K], `${Prefix}${K}.`>
: `${Prefix}${K}`;
}[keyof T & string]
: never;
```
Expand Down
2 changes: 1 addition & 1 deletion packages/qwik-city/src/runtime/src/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -497,7 +497,7 @@ export const validator$: ValidatorConstructor;
//
// @public (undocumented)
export type ValidatorErrorKeyDotNotation<T, Prefix extends string = ''> = IsAny<T> extends true ? never : T extends object ? {
[K in keyof T & string]: T[K] extends (infer U)[] ? U extends object ? `${Prefix}${K}[]` | `${Prefix}${K}[]${ValidatorErrorKeyDotNotation<U, '.'>}` : `${Prefix}${K}[]` : T[K] extends object ? ValidatorErrorKeyDotNotation<T[K], `${Prefix}${K}.`> : `${Prefix}${K}`;
[K in keyof T & string]: IsAny<T[K]> extends true ? never : T[K] extends (infer U)[] ? IsAny<U> extends true ? never : U extends object ? `${Prefix}${K}[]` | ValidatorErrorKeyDotNotation<U, `${Prefix}${K}[].`> : `${Prefix}${K}[]` : T[K] extends object ? ValidatorErrorKeyDotNotation<T[K], `${Prefix}${K}.`> : `${Prefix}${K}`;
}[keyof T & string] : never;

// @public (undocumented)
Expand Down
99 changes: 98 additions & 1 deletion packages/qwik-city/src/runtime/src/server-functions.unit.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { describe, expectTypeOf, test } from 'vitest';
import { z } from 'zod';
import { server$ } from './server-functions';
import type { RequestEventBase } from './types';
import type { RequestEventBase, ValidatorErrorType } from './types';

describe('types', () => {
test('matching', () => () => {
Expand Down Expand Up @@ -64,4 +65,100 @@ describe('types', () => {
}>
>();
});

test('easy zod type', () => () => {
const zodSchema = z.object({
username: z.string(),
password: z.string(),
});
type ErrorType = ValidatorErrorType<z.infer<typeof zodSchema>>['fieldErrors'];

expectTypeOf<ErrorType>().toEqualTypeOf<{
username?: string;
password?: string;
}>();
});

test('array zod type with string', () => () => {
const zodSchema = z.object({
arrayWithStrings: z.array(z.string()),
});
type ErrorType = ValidatorErrorType<z.infer<typeof zodSchema>>['fieldErrors'];

expectTypeOf<ErrorType>().toEqualTypeOf<{
['arrayWithStrings[]']?: string[];
}>();
});

test('array zod type with object', () => () => {
const zodSchema = z.object({
persons: z.array(
z.object({
name: z.string(),
age: z.number(),
})
),
});
type ErrorType = ValidatorErrorType<z.infer<typeof zodSchema>>['fieldErrors'];

expectTypeOf<ErrorType>().toEqualTypeOf<{
'persons[]'?: string[];
'persons[].name'?: string[];
'persons[].age'?: string[];
}>();
});

test('Complex zod type', () => () => {
const BaseUserSchema = z.object({
id: z.string().uuid(),
username: z.string().min(3).max(20),
email: z.string().email(),
createdAt: z.date().default(new Date()),
isActive: z.boolean().default(true),
someAnyType: z.any(),
roles: z.array(z.enum(['user', 'admin', 'moderator'])).default(['user']),
preferences: z
.object({
theme: z.enum(['light', 'dark']).default('light'),
notifications: z.boolean().default(true),
})
.optional(),
});

// Schema for an Admin user with additional fields
const AdminUserSchema = BaseUserSchema.extend({
adminSince: z.date(),
permissions: z.array(z.string()),
}).refine((data) => data.roles.includes('admin'), {
message: 'Admin role must be included in roles',
});

// Schema for a Moderator user with additional fields
const ModeratorUserSchema = BaseUserSchema.extend({
moderatedSections: z.array(z.string()),
}).refine((data) => data.roles.includes('moderator'), {
message: 'Moderator role must be included in roles',
});

// Union of all user types
const UserSchema = z.union([AdminUserSchema, ModeratorUserSchema, BaseUserSchema]);

type ErrorType = ValidatorErrorType<z.infer<typeof UserSchema>>['fieldErrors'];
type EqualType = {
username?: string;
id?: string;
email?: string;
isActive?: string;
preferences?: string;
'roles[]'?: string[];
'permissions[]'?: string[];
'moderatedSections[]'?: string[];
};

expectTypeOf<ErrorType>().toEqualTypeOf<EqualType>();

expectTypeOf<ErrorType>().not.toEqualTypeOf<{
someAnyType?: string;
}>();
});
});
18 changes: 11 additions & 7 deletions packages/qwik-city/src/runtime/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -386,13 +386,17 @@ export type ValidatorErrorKeyDotNotation<T, Prefix extends string = ''> =
? never
: T extends object
? {
[K in keyof T & string]: T[K] extends (infer U)[]
? U extends object
? `${Prefix}${K}[]` | `${Prefix}${K}[]${ValidatorErrorKeyDotNotation<U, '.'>}`
: `${Prefix}${K}[]`
: T[K] extends object
? ValidatorErrorKeyDotNotation<T[K], `${Prefix}${K}.`>
: `${Prefix}${K}`;
[K in keyof T & string]: IsAny<T[K]> extends true
? never
: T[K] extends (infer U)[]
? IsAny<U> extends true
? never
: U extends object
? `${Prefix}${K}[]` | ValidatorErrorKeyDotNotation<U, `${Prefix}${K}[].`>
: `${Prefix}${K}[]`
: T[K] extends object
? ValidatorErrorKeyDotNotation<T[K], `${Prefix}${K}.`>
: `${Prefix}${K}`;
}[keyof T & string]
: never;

Expand Down

0 comments on commit 6f63fee

Please sign in to comment.