Skip to content

Commit 6b835e0

Browse files
committed
Added FormPathArrays type.
1 parent 7c46d8d commit 6b835e0

File tree

6 files changed

+70
-46
lines changed

6 files changed

+70
-46
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,12 @@ Headlines: Added, Changed, Deprecated, Removed, Fixed, Security
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## [Unreleased]
9+
10+
### Added
11+
12+
- Added `FormPathArrays` type, enumerating all arrays in an object as string accessors.
13+
814
## [1.9.0] - 2023-10-31
915

1016
### Added

src/lib/index.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,11 @@ export type ZodValidation<T extends AnyZodObject> =
3434
| ZodEffects<ZodEffects<ZodEffects<ZodEffects<T>>>>
3535
| ZodEffects<ZodEffects<ZodEffects<ZodEffects<ZodEffects<T>>>>>;
3636

37-
export { type FormPath, type FormPathLeaves } from './stringPath.js';
37+
export {
38+
type FormPath,
39+
type FormPathLeaves,
40+
type FormPathArrays
41+
} from './stringPath.js';
3842

3943
export type RawShape<T> = T extends ZodObject<infer U> ? U : never;
4044

src/lib/stringPath.ts

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -21,39 +21,47 @@ export function mergePath(path: (string | number | symbol)[]) {
2121
/**
2222
* Lists all paths in an object as string accessors.
2323
*/
24-
export type FormPath<T extends object, OnlyArrays extends boolean = false> =
25-
| (string & StringPath<T, OnlyArrays>)
24+
export type FormPath<T extends object> =
25+
| (string & StringPath<T>)
2626
| FormPathLeaves<T>;
2727

2828
/**
29-
* Like FormPath, but only with non-objects as accessible properties.
29+
* List paths in an object as string accessors, but only with non-objects as accessible properties.
3030
* Similar to the leaves in a node tree, if you look at the object as a tree structure.
3131
*/
3232
export type FormPathLeaves<T extends object> = string & StringPathLeaves<T>;
3333

34+
/**
35+
* List all arrays in an object as string accessors.
36+
*/
37+
export type FormPathArrays<T extends object> = string &
38+
Exclude<StringPath<T, true>, FormPathLeaves<T>>;
39+
3440
export type StringPath<
3541
T extends object,
3642
OnlyArrays extends boolean = false
3743
> = NonNullable<T> extends (infer U)[]
3844
? NonNullable<U> extends object
3945
?
40-
| `[${number}]`
41-
| `[${number}]${U extends unknown[]
46+
| (OnlyArrays extends false
47+
? `[${number}]`
48+
: NonNullable<U> extends unknown[]
49+
? `[${number}]`
50+
: never)
51+
| `[${number}]${NonNullable<U> extends unknown[]
4252
? ''
4353
: '.'}${NonNullable<U> extends Date | Set<unknown>
4454
? never
45-
: StringPath<NonNullable<U>> & string}`
46-
: `[${number}]` /*| `[${number}].${U & string}`*/
55+
: StringPath<NonNullable<U>, OnlyArrays> & string}`
56+
: `[${number}]`
4757
: NonNullable<T> extends object
4858
?
4959
| (OnlyArrays extends false
5060
? keyof T
5161
: {
5262
[K in keyof T]-?: K extends string
53-
? NonNullable<T[K]> extends (infer U2)[]
54-
? U2 extends object
55-
? never
56-
: K
63+
? NonNullable<T[K]> extends unknown[]
64+
? K
5765
: never
5866
: never;
5967
}[keyof T])
@@ -64,7 +72,7 @@ export type StringPath<
6472
? ''
6573
: '.'}${NonNullable<T[K]> extends Date | Set<unknown>
6674
? never
67-
: StringPath<NonNullable<T[K]>> & string}`
75+
: StringPath<NonNullable<T[K]>, OnlyArrays> & string}`
6876
: never
6977
: never;
7078
}[keyof T]

src/paths.test-d.ts

Lines changed: 37 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,11 @@
22
import type {
33
StringPath,
44
FormPathType,
5+
FormPathArrays,
56
StringPathLeaves
67
} from '$lib/stringPath';
78
import { test } from 'vitest';
9+
import type { z } from 'zod';
810

911
type Obj = {
1012
name: string;
@@ -16,29 +18,53 @@ type Obj = {
1618
tags:
1719
| ({ id: number; name: string; parents: number[] } | null)[]
1820
| undefined;
21+
names: string[];
1922
};
2023

2124
const i = 7 + 3;
2225

2326
type Test = StringPath<Obj>;
27+
type Arrays = FormPathArrays<Obj>;
2428

2529
test('StringPath', () => {
26-
const a1: Test = 'name';
27-
const a2: Test = 'city';
28-
const a3: Test = 'tags';
29-
const a4: Test = 'city.name';
30-
const a5: Test = 'tags[3]';
31-
const a6: Test = 'tags[3].name';
32-
const a7: Test = 'scores[3][4]';
30+
const t1: Test = 'name';
31+
const t2: Test = 'city';
32+
const t3: Test = 'tags';
33+
const t4: Test = 'city.name';
34+
const t5: Test = 'tags[3]';
35+
const t6: Test = 'tags[3].name';
36+
const t7: Test = 'scores[3][4]';
37+
const t8: Test = 'names[3]';
3338

3439
// @ts-expect-error incorrect path
35-
const n8: Test = 'city[3]';
40+
const t1e: Test = 'city[3]';
3641
// @ts-expect-error incorrect path
37-
const n7: Test = 'city.nope';
42+
const t2e: Test = 'city.nope';
3843
// @ts-expect-error incorrect path
39-
const n9: Test = 'tags[4].nope';
44+
const t3e: Test = 'tags[4].nope';
4045
// @ts-expect-error incorrect path
41-
const n0: Test = 'nope';
46+
const t4e: Test = 'nope';
47+
// @ts-expect-error incorrect path
48+
const t5e: Test = 'names[2].test';
49+
50+
const a1: Arrays = 'scores';
51+
const a2: Arrays = 'scores[3]';
52+
const a3: Arrays = 'tags';
53+
const a4: Arrays = 'tags[2].parents';
54+
const a5: Arrays = 'names';
55+
56+
// @ts-expect-error incorrect path
57+
const a1e: Arrays = 'name';
58+
// @ts-expect-error incorrect path
59+
const a2e: Arrays = 'points';
60+
// @ts-expect-error incorrect path
61+
const a3e: Arrays = 'scores[2][1]';
62+
// @ts-expect-error incorrect path
63+
const a4e: Arrays = 'city';
64+
// @ts-expect-error incorrect path
65+
const a5e: Arrays = 'tags[1]';
66+
// @ts-expect-error incorrect path
67+
const a6e: Arrays = 'names[1]';
4268
});
4369

4470
function checkPath<T = never>() {

src/routes/tests/array-component/+page.svelte

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,8 @@
44
import SuperDebug from '$lib/client/SuperDebug.svelte';
55
import AutoComplete from './AutoComplete.svelte';
66
7-
import type { FormPath, FormPathLeaves } from '$lib';
8-
import type { schema } from './schema';
9-
import type { z } from 'zod';
10-
117
export let data: PageData;
128
13-
type FP = FormPath<z.infer<typeof schema>, true>;
14-
type FPL = FormPathLeaves<z.infer<typeof schema>>;
15-
16-
type OK = Exclude<FP, FPL>;
17-
189
const pageForm = superForm(data.form, {
1910
//dataType: 'json',
2011
//validators: schema
@@ -41,13 +32,5 @@
4132
<style lang="scss">
4233
form {
4334
margin: 2rem 0;
44-
45-
input {
46-
background-color: #dedede;
47-
}
48-
49-
.invalid {
50-
color: crimson;
51-
}
5235
}
5336
</style>

src/routes/tests/array-component/AutoComplete.svelte

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,12 @@
55

66
<script lang="ts" generics="T extends AnyZodObject">
77
import type { z } from 'zod';
8-
import type { ZodValidation, FormPath, FormPathLeaves } from '$lib';
8+
import type { ZodValidation, FormPath, FormPathArrays } from '$lib';
99
import type { Writable } from 'svelte/store';
1010
import { fieldProxy, type SuperForm } from '$lib/client';
1111
1212
export let form: SuperForm<ZodValidation<T>, unknown>;
13-
export let field: Exclude<
14-
FormPath<z.infer<T>>,
15-
FormPathLeaves<z.infer<T>>
16-
>;
13+
export let field: FormPathArrays<z.infer<T>>;
1714
export let options: { value: string; label: string }[];
1815
export let label = '';
1916

0 commit comments

Comments
 (0)