Skip to content

Commit b76506d

Browse files
committed
fix: infinite recursion with arrays
1 parent 214f9b8 commit b76506d

File tree

3 files changed

+40
-17
lines changed

3 files changed

+40
-17
lines changed

packages/react/src/test/types.test.tsx

+4-3
Original file line numberDiff line numberDiff line change
@@ -67,10 +67,11 @@ describe("types", () => {
6767

6868
it("should infer form scope correctly", () => {
6969
const Component = () => {
70-
const scope = {} as any as FormScope<Record<string, number>>;
70+
type Node = { value: number; children: Node[] };
71+
const scope = {} as any as FormScope<{ tree: Node }>;
7172
const form = useFormScope(scope);
72-
expectTypeOf(form.scope("bob")).toEqualTypeOf<FormScope<number>>();
73-
expectTypeOf(form.value("bob")).toEqualTypeOf<number>();
73+
expectTypeOf(form.scope("tree.value")).toEqualTypeOf<FormScope<number>>();
74+
expectTypeOf(form.value("tree.value")).toEqualTypeOf<number>();
7475
};
7576
});
7677

packages/set-get/src/types.test.ts

+21-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { describe, expect, expectTypeOf, it } from "vitest";
1+
import { describe, expectTypeOf, it } from "vitest";
22
import {
33
StringToPathTuple,
44
ValidStringPaths,
@@ -31,6 +31,26 @@ describe("ValueAtPath type", () => {
3131
expectTypeOf<res>().toMatchTypeOf<never>();
3232
});
3333

34+
it("should work with recursive types", () => {
35+
type Node = {
36+
value: number;
37+
children: Node[];
38+
};
39+
type Tree = {
40+
tree: Node;
41+
};
42+
expectTypeOf<
43+
ValueAtPath<Tree, ["tree", "children", 0, "value"]>
44+
>().toMatchTypeOf<number>();
45+
expectTypeOf<
46+
| "tree.value"
47+
| "tree.children"
48+
| `tree.children[${number}]`
49+
| `tree.children[${number}].value`
50+
| `tree.children[${number}].children`
51+
>().toMatchTypeOf<ValidStringPaths<Tree>>();
52+
});
53+
3454
it("should work with tuples", () => {
3555
type state = {
3656
a: {

packages/set-get/src/types.ts

+15-13
Original file line numberDiff line numberDiff line change
@@ -60,25 +60,27 @@ export type StringToPathTuple<S extends string> = StringToPathTupleImpl<
6060

6161
type Path<Obj, Prefix extends Array<PathKey> = [], AssignableTo = any> =
6262
| (Obj extends AssignableTo ? Prefix : never)
63-
| (Obj extends Primitive
63+
| (Prefix["length"] extends 10
6464
? never
65-
: IsAny<Obj> extends true // prevent infinite recursion when using `any` (usually for generic base types)
65+
: Obj extends Primitive
6666
? never
67-
: Obj extends Array<infer Item>
68-
? Path<Item, [...Prefix, number], AssignableTo>
69-
: PathsOfObject<Obj, Prefix, AssignableTo>);
67+
: IsAny<Obj> extends true // prevent infinite recursion when using `any` (usually for generic base types)
68+
? never
69+
: Obj extends Array<infer Item>
70+
? PathsOfArray<Item, Prefix, AssignableTo>
71+
: PathsOfObject<Obj, Prefix, AssignableTo>);
7072

71-
type PathsOfObject<
73+
type PathsOfArray<
7274
Obj,
7375
Prefix extends Array<PathKey>,
7476
AssignableTo = any,
75-
> = Prefix["length"] extends 10
76-
? never
77-
: {
78-
[K in keyof Obj]: K extends PathKey
79-
? Path<Obj[K], [...Prefix, K], AssignableTo>
80-
: never;
81-
}[keyof Obj];
77+
> = Path<Obj, [...Prefix, number], AssignableTo>;
78+
79+
type PathsOfObject<Obj, Prefix extends Array<PathKey>, AssignableTo = any> = {
80+
[K in keyof Obj]: K extends PathKey
81+
? Path<Obj[K], [...Prefix, K], AssignableTo>
82+
: never;
83+
}[keyof Obj];
8284

8385
export type ValueAtPath<
8486
Obj,

0 commit comments

Comments
 (0)