@tanstack/form-core
Advanced tools
Comparing version 0.16.0 to 0.16.1
@@ -5,3 +5,3 @@ import { Store } from '@tanstack/store'; | ||
import type { Updater } from './utils.js'; | ||
import type { DeepKeys, DeepValue } from './util-types.js'; | ||
import type { DeepKeys, DeepValue, NoInfer } from './util-types.js'; | ||
export type FieldValidateFn<TParentData, TName extends DeepKeys<TParentData>, TFieldValidator extends Validator<DeepValue<TParentData, TName>, unknown> | undefined = undefined, TFormValidator extends Validator<TParentData, unknown> | undefined = undefined, TData extends DeepValue<TParentData, TName> = DeepValue<TParentData, TName>> = (props: { | ||
@@ -32,4 +32,3 @@ value: TData; | ||
name: TName; | ||
index?: TData extends any[] ? number : never; | ||
defaultValue?: TData; | ||
defaultValue?: NoInfer<TData>; | ||
asyncDebounceMs?: number; | ||
@@ -36,0 +35,0 @@ asyncAlways?: boolean; |
@@ -261,3 +261,6 @@ import { Store } from "@tanstack/store"; | ||
if (adapter && typeof props.validate !== "function") { | ||
return adapter()[props.type](props.value, props.validate); | ||
return adapter()[props.type]( | ||
props.value, | ||
props.validate | ||
); | ||
} | ||
@@ -264,0 +267,0 @@ } |
@@ -6,2 +6,3 @@ export type RequiredByKey<T, K extends keyof T> = Omit<T, K> & Required<Pick<T, K>>; | ||
}; | ||
export type NoInfer<T> = [T][T extends any ? 0 : never]; | ||
export type Narrow<A> = Try<A, [], NarrowRaw<A>>; | ||
@@ -29,3 +30,16 @@ type Try<A1, A2, Catch = never> = A1 extends A2 ? A1 : Catch; | ||
type PrefixFromDepth<T extends string | number, TDepth extends any[]> = TDepth['length'] extends 0 ? T : `.${T}`; | ||
export type DeepValue<TValue, TAccessor> = TValue extends Record<string | number, any> ? TAccessor extends `${infer TBefore}[${infer TBrackets}].${infer TAfter}` ? DeepValue<TValue[TBefore][TBrackets], TAfter> : TAccessor extends `[${infer TBrackets}]` ? DeepValue<TValue, TBrackets> : TAccessor extends `${infer TBefore}[${infer TBrackets}]` ? DeepValue<TValue[TBefore], TBrackets> : TAccessor extends `${infer TBefore}.${infer TAfter}` ? DeepValue<TValue[TBefore], TAfter> : TValue[TAccessor & string] : never; | ||
/** | ||
Infer the type of a deeply nested property within an object or an array. | ||
See the tests in `util-types.test-d.ts` for usage. | ||
*/ | ||
export type DeepValue<TValue, TAccessor, TDepth extends any[] = []> = TDepth['length'] extends 3 ? never : TValue extends ReadonlyArray<any> ? TAccessor extends `[${infer TBrackets}].${infer TAfter}` ? DeepValue<DeepValue<TValue, TBrackets, [...TDepth, any]>, TAfter, [ | ||
...TDepth, | ||
any | ||
]> : TAccessor extends `[${infer TBrackets}]` ? DeepValue<TValue, TBrackets, [...TDepth, any]> : TAccessor extends keyof TValue ? TValue[TAccessor] : TValue[TAccessor & number] : TValue extends Record<string | number, any> ? TAccessor extends `${infer TBefore}[${infer TEverythingElse}` ? DeepValue<DeepValue<TValue, TBefore, [...TDepth, any]>, `[${TEverythingElse}`, [ | ||
...TDepth, | ||
any | ||
]> : TAccessor extends `[${infer TBrackets}]` ? DeepValue<TValue, TBrackets, [...TDepth, any]> : TAccessor extends `${infer TBefore}.${infer TAfter}` ? DeepValue<DeepValue<TValue, TBefore, [...TDepth, any]>, TAfter, [ | ||
...TDepth, | ||
any | ||
]> : TAccessor extends string ? TValue[TAccessor] : never : never; | ||
export {}; |
{ | ||
"name": "@tanstack/form-core", | ||
"version": "0.16.0", | ||
"version": "0.16.1", | ||
"description": "Powerful, type-safe, framework agnostic forms.", | ||
@@ -44,3 +44,5 @@ "author": "tannerlinsley", | ||
"test:types:versions51": "node ../../node_modules/typescript51/lib/tsc.js", | ||
"test:types:versions52": "tsc", | ||
"test:types:versions52": "node ../../node_modules/typescript52/lib/tsc.js", | ||
"test:types:versions53": "node ../../node_modules/typescript53/lib/tsc.js", | ||
"test:types:versions54": "tsc", | ||
"test:types": "pnpm run \"/^test:types:versions.*/\"", | ||
@@ -47,0 +49,0 @@ "test:lib": "vitest", |
@@ -11,3 +11,3 @@ import { Store } from '@tanstack/store' | ||
import type { Updater } from './utils' | ||
import type { DeepKeys, DeepValue } from './util-types' | ||
import type { DeepKeys, DeepValue, NoInfer } from './util-types' | ||
@@ -198,4 +198,3 @@ export type FieldValidateFn< | ||
name: TName | ||
index?: TData extends any[] ? number : never | ||
defaultValue?: TData | ||
defaultValue?: NoInfer<TData> | ||
asyncDebounceMs?: number | ||
@@ -356,3 +355,6 @@ asyncAlways?: boolean | ||
if (adapter && typeof props.validate !== 'function') { | ||
return adapter()[props.type](props.value, props.validate) as never | ||
return adapter()[props.type]( | ||
props.value as never, | ||
props.validate, | ||
) as never | ||
} | ||
@@ -443,3 +445,3 @@ } | ||
getValue = (): TData => { | ||
return this.form.getFieldValue(this.name) as any | ||
return this.form.getFieldValue(this.name) as TData | ||
} | ||
@@ -446,0 +448,0 @@ |
@@ -121,1 +121,30 @@ import { assertType, it } from 'vitest' | ||
}) | ||
it('should type an array sub-field properly', () => { | ||
type Person = { | ||
name: string | ||
age: number | ||
} | ||
const form = new FormApi({ | ||
defaultValues: { | ||
nested: { | ||
people: [] as Person[], | ||
}, | ||
}, | ||
} as const) | ||
const field = new FieldApi({ | ||
form, | ||
name: `nested.people[${1}].name`, | ||
validators: { | ||
onChangeAsync: async ({ value }) => { | ||
assertType<string>(value) | ||
return undefined | ||
}, | ||
}, | ||
}) | ||
assertType<string>(field.state.value) | ||
}) |
@@ -90,2 +90,20 @@ import { assertType } from 'vitest' | ||
type NestedTupleBroadExample = DeepValue< | ||
{ topUsers: User[] }, | ||
`topUsers[${number}].age` | ||
> | ||
assertType<number>(0 as never as NestedTupleBroadExample) | ||
type DeeplyNestedTupleBroadExample = DeepValue< | ||
{ nested: { topUsers: User[] } }, | ||
`nested.topUsers[${number}].age` | ||
> | ||
assertType<number>(0 as never as DeeplyNestedTupleBroadExample) | ||
type SimpleArrayExample = DeepValue<User[], `[${number}]`> | ||
assertType<User>(0 as never as SimpleArrayExample) | ||
type SimpleNestedArrayExample = DeepValue<User[], `[${number}].age`> | ||
assertType<number>(0 as never as SimpleNestedArrayExample) | ||
type NestedTupleItemExample = DeepValue< | ||
@@ -108,1 +126,14 @@ { topUsers: [User, 0, User] }, | ||
} | ||
type FormDefinition = { | ||
nested: { | ||
people: User[] | ||
} | ||
} | ||
type FormDefinitionValue = DeepValue< | ||
FormDefinition, | ||
`nested.people[${number}].name` | ||
> | ||
assertType<string>(0 as never as FormDefinitionValue) |
@@ -13,2 +13,4 @@ export type RequiredByKey<T, K extends keyof T> = Omit<T, K> & | ||
export type NoInfer<T> = [T][T extends any ? 0 : never] | ||
export type Narrow<A> = Try<A, [], NarrowRaw<A>> | ||
@@ -87,15 +89,54 @@ | ||
export type DeepValue<TValue, TAccessor> = TValue extends Record< | ||
string | number, | ||
any | ||
> | ||
? TAccessor extends `${infer TBefore}[${infer TBrackets}].${infer TAfter}` | ||
? DeepValue<TValue[TBefore][TBrackets], TAfter> | ||
: TAccessor extends `[${infer TBrackets}]` | ||
? DeepValue<TValue, TBrackets> | ||
: TAccessor extends `${infer TBefore}[${infer TBrackets}]` | ||
? DeepValue<TValue[TBefore], TBrackets> | ||
: TAccessor extends `${infer TBefore}.${infer TAfter}` | ||
? DeepValue<TValue[TBefore], TAfter> | ||
: TValue[TAccessor & string] | ||
: never | ||
/** | ||
Infer the type of a deeply nested property within an object or an array. | ||
See the tests in `util-types.test-d.ts` for usage. | ||
*/ | ||
export type DeepValue< | ||
// The object or array in which we have the property whose type we're trying to infer | ||
TValue, | ||
// A string representing the path of the property we're trying to access | ||
TAccessor, | ||
// (Optional) An array representing the current depth of recursion | ||
TDepth extends any[] = [], | ||
> = | ||
// Limit the depth of recursion to 3 levels | ||
TDepth['length'] extends 3 | ||
? never | ||
: // Check if we're looking for the property in an array | ||
TValue extends ReadonlyArray<any> | ||
? TAccessor extends `[${infer TBrackets}].${infer TAfter}` | ||
? /* | ||
Extract the first element from the accessor path (`TBrackets`) | ||
and recursively call `DeepValue` with it | ||
*/ | ||
DeepValue< | ||
DeepValue<TValue, TBrackets, [...TDepth, any]>, | ||
TAfter, | ||
[...TDepth, any] | ||
> | ||
: TAccessor extends `[${infer TBrackets}]` | ||
? DeepValue<TValue, TBrackets, [...TDepth, any]> | ||
: TAccessor extends keyof TValue | ||
? TValue[TAccessor] | ||
: TValue[TAccessor & number] | ||
: // Check if we're looking for the property in an object | ||
TValue extends Record<string | number, any> | ||
? TAccessor extends `${infer TBefore}[${infer TEverythingElse}` | ||
? DeepValue< | ||
DeepValue<TValue, TBefore, [...TDepth, any]>, | ||
`[${TEverythingElse}`, | ||
[...TDepth, any] | ||
> | ||
: TAccessor extends `[${infer TBrackets}]` | ||
? DeepValue<TValue, TBrackets, [...TDepth, any]> | ||
: TAccessor extends `${infer TBefore}.${infer TAfter}` | ||
? DeepValue< | ||
DeepValue<TValue, TBefore, [...TDepth, any]>, | ||
TAfter, | ||
[...TDepth, any] | ||
> | ||
: TAccessor extends string | ||
? TValue[TAccessor] | ||
: never | ||
: // Do not allow `TValue` to be anything else | ||
never |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
347110
5763