Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 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
3 changes: 3 additions & 0 deletions knip.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@
"entry": ["**/vite.config.solid.ts", "**/src/solid/**"],
"project": ["**/vite.config.solid.ts", "**/src/solid/**"]
},
"packages/devtools-ui": {
"entry": ["**/tests/**/*.ts"]
},
"packages/solid-devtools": {
"ignore": ["**/core.tsx"]
}
Expand Down
38 changes: 25 additions & 13 deletions packages/devtools-ui/src/utils/deep-keys.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,30 @@
type IsAny<T> = 0 extends 1 & T ? true : false

type Clean<T> = Exclude<T, undefined | null>

type CollapsibleKeys<T, TPrefix extends string = ''> =
T extends ReadonlyArray<infer U>
?
| (TPrefix extends '' ? '' : TPrefix)
| CollapsibleKeys<U, `${TPrefix}[${number}]`>
: T extends object
IsAny<T> extends true
? TPrefix extends ''
? never
: TPrefix
: T extends ReadonlyArray<infer U>
?
| (TPrefix extends '' ? '' : TPrefix)
| {
[K in Extract<keyof T, string>]: CollapsibleKeys<
T[K],
TPrefix extends '' ? `${K}` : `${TPrefix}.${K}`
>
}[Extract<keyof T, string>]
: never
| CollapsibleKeys<U, `${TPrefix}[${number}]`>
: T extends object
?
| (TPrefix extends '' ? '' : TPrefix)
| {
[K in Extract<keyof T, string>]: CollapsibleKeys<
T[K],
TPrefix extends '' ? `${K}` : `${TPrefix}.${K}`
>
}[Extract<keyof T, string>]
: never

export type CollapsiblePaths<T> =
CollapsibleKeys<T> extends string ? CollapsibleKeys<T> : never
CollapsibleKeys<Clean<T>> extends infer P
? P extends string
? P
: never
: never
122 changes: 122 additions & 0 deletions packages/devtools-ui/tests/deep-keys.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
import { describe, expectTypeOf, it } from 'vitest'

// types
import type { CollapsiblePaths } from '../src/utils/deep-keys'

type WithDeeplyNestedObject = {
a: {
b: {
c: {
d: {
e: {
f: {
g: {
h: {
i: {
j: number
}
}
}
}
}
}
}
}
}
}

type _DeeplyNestedObject = CollapsiblePaths<WithDeeplyNestedObject>

type WithAny = {
errors?: any
}

type _Any = CollapsiblePaths<WithAny>

type ArrayRecursion = { arr: Array<Array<Array<Array<[]>>>> }

type _ArrayRecursion = CollapsiblePaths<ArrayRecursion>

type WithUndefined = {
status?: {
valid: boolean
error?: {
message: string
}
}
}

type _WithUndefined = CollapsiblePaths<WithUndefined>

type WithUnknown = {
payload: unknown
}

type _WithUnknown = CollapsiblePaths<WithUnknown>

type WithRealisticState = {
canSubmit?: boolean
isSubmitting?: boolean
errors?: Array<any>
errorMap?: Record<string, any>
}

type _WithRealisticState = CollapsiblePaths<WithRealisticState>

type WithGeneric<TData> = {
generic: TData
}

type _WithGeneric = CollapsiblePaths<WithGeneric<{ a: { b: string } }>>

describe('deep-keys', () => {
it('should type deeply nested keys', () => {
expectTypeOf<_DeeplyNestedObject>().toEqualTypeOf<
| ''
| 'a'
| 'a.b'
| 'a.b.c'
| 'a.b.c.d'
| 'a.b.c.d.e'
| 'a.b.c.d.e.f'
| 'a.b.c.d.e.f.g'
| 'a.b.c.d.e.f.g.h'
| 'a.b.c.d.e.f.g.h.i'
>()
})

it('should handle any', () => {
expectTypeOf<_Any>().toEqualTypeOf<'' | 'errors'>()
})

it('should handle array recursion', () => {
expectTypeOf<_ArrayRecursion>().toEqualTypeOf<
| ''
| 'arr'
| `arr[${number}]`
| `arr[${number}][${number}]`
| `arr[${number}][${number}][${number}]`
| `arr[${number}][${number}][${number}][${number}]`
>()
})

it('should handle undefined', () => {
expectTypeOf<_WithUndefined>().toEqualTypeOf<
'' | 'status' | 'status.error'
>()
})

it('should handle unknown', () => {
expectTypeOf<_WithUnknown>().toEqualTypeOf<''>()
})

it('should handle realistic state', () => {
expectTypeOf<_WithRealisticState>().toEqualTypeOf<
'' | 'errors' | 'errorMap' | `errors[${number}]` | `errorMap.${string}`
>()
})

it('should handle generics', () => {
expectTypeOf<_WithGeneric>().toEqualTypeOf<'' | 'generic' | 'generic.a'>()
})
})
Loading