@tanstack/form-core 0.3.4 → 0.3.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -19,7 +19,7 @@ type FormOptions<TData> = {
19
19
  onSubmitInvalid?: (values: TData, formApi: FormApi<TData>) => void;
20
20
  };
21
21
  type FieldInfo<TFormData> = {
22
- instances: Record<string, FieldApi<any, TFormData>>;
22
+ instances: Record<string, FieldApi<TFormData, any, any>>;
23
23
  } & ValidationMeta;
24
24
  type ValidationMeta = {
25
25
  validationCount?: number;
@@ -64,7 +64,7 @@ declare class FormApi<TFormData> {
64
64
  handleSubmit: () => Promise<void>;
65
65
  getFieldValue: <TField extends DeepKeys<TFormData>>(field: TField) => DeepValue<TFormData, TField>;
66
66
  getFieldMeta: <TField extends DeepKeys<TFormData>>(field: TField) => FieldMeta | undefined;
67
- getFieldInfo: <TField extends DeepKeys<TFormData>>(field: TField) => Record<DeepKeys<TFormData>, FieldInfo<TFormData>>[TField];
67
+ getFieldInfo: <TField extends DeepKeys<TFormData>>(field: TField) => FieldInfo<TFormData>;
68
68
  setFieldMeta: <TField extends DeepKeys<TFormData>>(field: TField, updater: Updater<FieldMeta>) => void;
69
69
  setFieldValue: <TField extends DeepKeys<TFormData>>(field: TField, updater: Updater<DeepValue<TFormData, TField>>, opts?: {
70
70
  touch?: boolean;
@@ -82,44 +82,35 @@ declare class FormApi<TFormData> {
82
82
  }
83
83
 
84
84
  type ValidationCause = 'change' | 'blur' | 'submit' | 'mount';
85
- type ValidateFn<TData, TFormData> = (value: TData, fieldApi: FieldApi<TData, TFormData>) => ValidationError;
86
- type ValidateAsyncFn<TData, TFormData> = (value: TData, fieldApi: FieldApi<TData, TFormData>) => ValidationError | Promise<ValidationError>;
87
- interface FieldOptions<_TData, TFormData,
85
+ type ValidateFn<TParentData, TName extends DeepKeys<TParentData>, TData> = (value: TData, fieldApi: FieldApi<TParentData, TName>) => ValidationError;
86
+ type ValidateAsyncFn<TParentData, TName extends DeepKeys<TParentData>, TData> = (value: TData, fieldApi: FieldApi<TParentData, TName>) => ValidationError | Promise<ValidationError>;
87
+ interface FieldOptions<TParentData,
88
88
  /**
89
89
  * This allows us to restrict the name to only be a valid field name while
90
90
  * also assigning it to a generic
91
91
  */
92
- TName = unknown extends TFormData ? string : DeepKeys<TFormData>,
92
+ TName extends DeepKeys<TParentData>,
93
93
  /**
94
94
  * If TData is unknown, we can use the TName generic to determine the type
95
95
  */
96
- TData = unknown extends _TData ? DeepValue<TFormData, TName> : _TData> {
97
- name: TName;
96
+ TData = DeepValue<TParentData, TName>> {
97
+ name: DeepKeys<TParentData>;
98
98
  index?: TData extends any[] ? number : never;
99
99
  defaultValue?: TData;
100
100
  asyncDebounceMs?: number;
101
101
  asyncAlways?: boolean;
102
- onMount?: (formApi: FieldApi<TData, TFormData>) => void;
103
- onChange?: ValidateFn<TData, TFormData>;
104
- onChangeAsync?: ValidateAsyncFn<TData, TFormData>;
102
+ onMount?: (formApi: FieldApi<TParentData, TName>) => void;
103
+ onChange?: ValidateFn<TParentData, TName, TData>;
104
+ onChangeAsync?: ValidateAsyncFn<TParentData, TName, TData>;
105
105
  onChangeAsyncDebounceMs?: number;
106
- onBlur?: ValidateFn<TData, TFormData>;
107
- onBlurAsync?: ValidateAsyncFn<TData, TFormData>;
106
+ onBlur?: ValidateFn<TParentData, TName, TData>;
107
+ onBlurAsync?: ValidateAsyncFn<TParentData, TName, TData>;
108
108
  onBlurAsyncDebounceMs?: number;
109
- onSubmitAsync?: ValidateAsyncFn<TData, TFormData>;
109
+ onSubmitAsync?: ValidateAsyncFn<TParentData, TName, TData>;
110
110
  defaultMeta?: Partial<FieldMeta>;
111
111
  }
112
- interface FieldApiOptions<_TData, TFormData,
113
- /**
114
- * This allows us to restrict the name to only be a valid field name while
115
- * also assigning it to a generic
116
- */
117
- TName = unknown extends TFormData ? string : DeepKeys<TFormData>,
118
- /**
119
- * If TData is unknown, we can use the TName generic to determine the type
120
- */
121
- TData = unknown extends _TData ? DeepValue<TFormData, TName> : _TData> extends FieldOptions<_TData, TFormData, TName, TData> {
122
- form: FormApi<TFormData>;
112
+ interface FieldApiOptions<TParentData, TName extends DeepKeys<TParentData>, TData = DeepValue<TParentData, TName>> extends FieldOptions<TParentData, TName, TData> {
113
+ form: FormApi<TParentData>;
123
114
  }
124
115
  type FieldMeta = {
125
116
  isTouched: boolean;
@@ -132,21 +123,21 @@ type FieldState<TData> = {
132
123
  value: TData;
133
124
  meta: FieldMeta;
134
125
  };
135
- type GetTData<TData, TFormData, Opts extends FieldApiOptions<TData, TFormData>> = Opts extends FieldApiOptions<infer _TData, infer _TFormData, infer _TName, infer RealTData> ? RealTData : never;
136
- declare class FieldApi<_TData, TFormData, Opts extends FieldApiOptions<_TData, TFormData> = FieldApiOptions<_TData, TFormData>, TData extends GetTData<_TData, TFormData, Opts> = GetTData<_TData, TFormData, Opts>> {
126
+ type ResolveName<TParentData> = unknown extends TParentData ? string : DeepKeys<TParentData>;
127
+ declare class FieldApi<TParentData, TName extends DeepKeys<TParentData>, TData = DeepValue<TParentData, TName>> {
137
128
  #private;
138
129
  uid: number;
139
- form: Opts['form'];
140
- name: DeepKeys<TFormData>;
141
- options: Opts;
130
+ form: FieldApiOptions<TParentData, TName, TData>['form'];
131
+ name: DeepKeys<TParentData>;
132
+ options: FieldApiOptions<TParentData, TName>;
142
133
  store: Store<FieldState<TData>>;
143
134
  state: FieldState<TData>;
144
135
  prevState: FieldState<TData>;
145
- constructor(opts: Opts & {
146
- form: FormApi<TFormData>;
136
+ constructor(opts: FieldApiOptions<TParentData, TName, TData> & {
137
+ form: FormApi<TParentData>;
147
138
  });
148
139
  mount: () => () => void;
149
- update: (opts: FieldApiOptions<TData, TFormData>) => void;
140
+ update: (opts: FieldApiOptions<TParentData, TName, TData>) => void;
150
141
  getValue: () => TData;
151
142
  setValue: (updater: Updater<TData>, options?: {
152
143
  touch?: boolean;
@@ -155,12 +146,12 @@ declare class FieldApi<_TData, TFormData, Opts extends FieldApiOptions<_TData, T
155
146
  _getMeta: () => FieldMeta | undefined;
156
147
  getMeta: () => FieldMeta;
157
148
  setMeta: (updater: Updater<FieldMeta>) => void;
158
- getInfo: () => Record<DeepKeys<TFormData>, FieldInfo<TFormData>>[DeepKeys<TFormData>];
149
+ getInfo: () => FieldInfo<TParentData>;
159
150
  pushValue: (value: TData extends any[] ? TData[number] : never) => void;
160
151
  insertValue: (index: number, value: TData extends any[] ? TData[number] : never) => void;
161
152
  removeValue: (index: number) => void;
162
153
  swapValues: (aIndex: number, bIndex: number) => void;
163
- getSubField: <TName extends DeepKeys<TData>>(name: TName) => FieldApi<DeepValue<TData, TName>, TFormData, FieldApiOptions<DeepValue<TData, TName>, TFormData, unknown extends TFormData ? string : DeepKeys<TFormData>, unknown extends DeepValue<TData, TName> ? DeepValue<TFormData, unknown extends TFormData ? string : DeepKeys<TFormData>> : DeepValue<TData, TName>>, unknown extends DeepValue<TData, TName> ? DeepValue<TFormData, unknown extends TFormData ? string : DeepKeys<TFormData>> : DeepValue<TData, TName>>;
154
+ getSubField: <TSubName extends DeepKeys<TData>, TSubData = DeepValue<TData, TSubName>>(name: TSubName) => FieldApi<TData, TSubName, TSubData>;
164
155
  validateSync: (value: TData | undefined, cause: ValidationCause) => void;
165
156
  cancelValidateAsync: () => void;
166
157
  validateAsync: (value: TData | undefined, cause: ValidationCause) => Promise<ValidationError[]>;
@@ -169,4 +160,4 @@ declare class FieldApi<_TData, TFormData, Opts extends FieldApiOptions<_TData, T
169
160
  handleBlur: () => void;
170
161
  }
171
162
 
172
- export { DeepKeys, DeepValue, FieldApi, FieldApiOptions, FieldInfo, FieldMeta, FieldOptions, FieldState, FormApi, FormOptions, FormState, Updater, ValidationCause, ValidationError, ValidationErrorMap, ValidationErrorMapKeys, ValidationMeta };
163
+ export { DeepKeys, DeepValue, FieldApi, FieldApiOptions, FieldInfo, FieldMeta, FieldOptions, FieldState, FormApi, FormOptions, FormState, ResolveName, Updater, ValidationCause, ValidationError, ValidationErrorMap, ValidationErrorMapKeys, ValidationMeta };
@@ -19,7 +19,7 @@ type FormOptions<TData> = {
19
19
  onSubmitInvalid?: (values: TData, formApi: FormApi<TData>) => void;
20
20
  };
21
21
  type FieldInfo<TFormData> = {
22
- instances: Record<string, FieldApi<any, TFormData>>;
22
+ instances: Record<string, FieldApi<TFormData, any, any>>;
23
23
  } & ValidationMeta;
24
24
  type ValidationMeta = {
25
25
  validationCount?: number;
@@ -64,7 +64,7 @@ declare class FormApi<TFormData> {
64
64
  handleSubmit: () => Promise<void>;
65
65
  getFieldValue: <TField extends DeepKeys<TFormData>>(field: TField) => DeepValue<TFormData, TField>;
66
66
  getFieldMeta: <TField extends DeepKeys<TFormData>>(field: TField) => FieldMeta | undefined;
67
- getFieldInfo: <TField extends DeepKeys<TFormData>>(field: TField) => Record<DeepKeys<TFormData>, FieldInfo<TFormData>>[TField];
67
+ getFieldInfo: <TField extends DeepKeys<TFormData>>(field: TField) => FieldInfo<TFormData>;
68
68
  setFieldMeta: <TField extends DeepKeys<TFormData>>(field: TField, updater: Updater<FieldMeta>) => void;
69
69
  setFieldValue: <TField extends DeepKeys<TFormData>>(field: TField, updater: Updater<DeepValue<TFormData, TField>>, opts?: {
70
70
  touch?: boolean;
@@ -82,44 +82,35 @@ declare class FormApi<TFormData> {
82
82
  }
83
83
 
84
84
  type ValidationCause = 'change' | 'blur' | 'submit' | 'mount';
85
- type ValidateFn<TData, TFormData> = (value: TData, fieldApi: FieldApi<TData, TFormData>) => ValidationError;
86
- type ValidateAsyncFn<TData, TFormData> = (value: TData, fieldApi: FieldApi<TData, TFormData>) => ValidationError | Promise<ValidationError>;
87
- interface FieldOptions<_TData, TFormData,
85
+ type ValidateFn<TParentData, TName extends DeepKeys<TParentData>, TData> = (value: TData, fieldApi: FieldApi<TParentData, TName>) => ValidationError;
86
+ type ValidateAsyncFn<TParentData, TName extends DeepKeys<TParentData>, TData> = (value: TData, fieldApi: FieldApi<TParentData, TName>) => ValidationError | Promise<ValidationError>;
87
+ interface FieldOptions<TParentData,
88
88
  /**
89
89
  * This allows us to restrict the name to only be a valid field name while
90
90
  * also assigning it to a generic
91
91
  */
92
- TName = unknown extends TFormData ? string : DeepKeys<TFormData>,
92
+ TName extends DeepKeys<TParentData>,
93
93
  /**
94
94
  * If TData is unknown, we can use the TName generic to determine the type
95
95
  */
96
- TData = unknown extends _TData ? DeepValue<TFormData, TName> : _TData> {
97
- name: TName;
96
+ TData = DeepValue<TParentData, TName>> {
97
+ name: DeepKeys<TParentData>;
98
98
  index?: TData extends any[] ? number : never;
99
99
  defaultValue?: TData;
100
100
  asyncDebounceMs?: number;
101
101
  asyncAlways?: boolean;
102
- onMount?: (formApi: FieldApi<TData, TFormData>) => void;
103
- onChange?: ValidateFn<TData, TFormData>;
104
- onChangeAsync?: ValidateAsyncFn<TData, TFormData>;
102
+ onMount?: (formApi: FieldApi<TParentData, TName>) => void;
103
+ onChange?: ValidateFn<TParentData, TName, TData>;
104
+ onChangeAsync?: ValidateAsyncFn<TParentData, TName, TData>;
105
105
  onChangeAsyncDebounceMs?: number;
106
- onBlur?: ValidateFn<TData, TFormData>;
107
- onBlurAsync?: ValidateAsyncFn<TData, TFormData>;
106
+ onBlur?: ValidateFn<TParentData, TName, TData>;
107
+ onBlurAsync?: ValidateAsyncFn<TParentData, TName, TData>;
108
108
  onBlurAsyncDebounceMs?: number;
109
- onSubmitAsync?: ValidateAsyncFn<TData, TFormData>;
109
+ onSubmitAsync?: ValidateAsyncFn<TParentData, TName, TData>;
110
110
  defaultMeta?: Partial<FieldMeta>;
111
111
  }
112
- interface FieldApiOptions<_TData, TFormData,
113
- /**
114
- * This allows us to restrict the name to only be a valid field name while
115
- * also assigning it to a generic
116
- */
117
- TName = unknown extends TFormData ? string : DeepKeys<TFormData>,
118
- /**
119
- * If TData is unknown, we can use the TName generic to determine the type
120
- */
121
- TData = unknown extends _TData ? DeepValue<TFormData, TName> : _TData> extends FieldOptions<_TData, TFormData, TName, TData> {
122
- form: FormApi<TFormData>;
112
+ interface FieldApiOptions<TParentData, TName extends DeepKeys<TParentData>, TData = DeepValue<TParentData, TName>> extends FieldOptions<TParentData, TName, TData> {
113
+ form: FormApi<TParentData>;
123
114
  }
124
115
  type FieldMeta = {
125
116
  isTouched: boolean;
@@ -132,21 +123,21 @@ type FieldState<TData> = {
132
123
  value: TData;
133
124
  meta: FieldMeta;
134
125
  };
135
- type GetTData<TData, TFormData, Opts extends FieldApiOptions<TData, TFormData>> = Opts extends FieldApiOptions<infer _TData, infer _TFormData, infer _TName, infer RealTData> ? RealTData : never;
136
- declare class FieldApi<_TData, TFormData, Opts extends FieldApiOptions<_TData, TFormData> = FieldApiOptions<_TData, TFormData>, TData extends GetTData<_TData, TFormData, Opts> = GetTData<_TData, TFormData, Opts>> {
126
+ type ResolveName<TParentData> = unknown extends TParentData ? string : DeepKeys<TParentData>;
127
+ declare class FieldApi<TParentData, TName extends DeepKeys<TParentData>, TData = DeepValue<TParentData, TName>> {
137
128
  #private;
138
129
  uid: number;
139
- form: Opts['form'];
140
- name: DeepKeys<TFormData>;
141
- options: Opts;
130
+ form: FieldApiOptions<TParentData, TName, TData>['form'];
131
+ name: DeepKeys<TParentData>;
132
+ options: FieldApiOptions<TParentData, TName>;
142
133
  store: Store<FieldState<TData>>;
143
134
  state: FieldState<TData>;
144
135
  prevState: FieldState<TData>;
145
- constructor(opts: Opts & {
146
- form: FormApi<TFormData>;
136
+ constructor(opts: FieldApiOptions<TParentData, TName, TData> & {
137
+ form: FormApi<TParentData>;
147
138
  });
148
139
  mount: () => () => void;
149
- update: (opts: FieldApiOptions<TData, TFormData>) => void;
140
+ update: (opts: FieldApiOptions<TParentData, TName, TData>) => void;
150
141
  getValue: () => TData;
151
142
  setValue: (updater: Updater<TData>, options?: {
152
143
  touch?: boolean;
@@ -155,12 +146,12 @@ declare class FieldApi<_TData, TFormData, Opts extends FieldApiOptions<_TData, T
155
146
  _getMeta: () => FieldMeta | undefined;
156
147
  getMeta: () => FieldMeta;
157
148
  setMeta: (updater: Updater<FieldMeta>) => void;
158
- getInfo: () => Record<DeepKeys<TFormData>, FieldInfo<TFormData>>[DeepKeys<TFormData>];
149
+ getInfo: () => FieldInfo<TParentData>;
159
150
  pushValue: (value: TData extends any[] ? TData[number] : never) => void;
160
151
  insertValue: (index: number, value: TData extends any[] ? TData[number] : never) => void;
161
152
  removeValue: (index: number) => void;
162
153
  swapValues: (aIndex: number, bIndex: number) => void;
163
- getSubField: <TName extends DeepKeys<TData>>(name: TName) => FieldApi<DeepValue<TData, TName>, TFormData, FieldApiOptions<DeepValue<TData, TName>, TFormData, unknown extends TFormData ? string : DeepKeys<TFormData>, unknown extends DeepValue<TData, TName> ? DeepValue<TFormData, unknown extends TFormData ? string : DeepKeys<TFormData>> : DeepValue<TData, TName>>, unknown extends DeepValue<TData, TName> ? DeepValue<TFormData, unknown extends TFormData ? string : DeepKeys<TFormData>> : DeepValue<TData, TName>>;
154
+ getSubField: <TSubName extends DeepKeys<TData>, TSubData = DeepValue<TData, TSubName>>(name: TSubName) => FieldApi<TData, TSubName, TSubData>;
164
155
  validateSync: (value: TData | undefined, cause: ValidationCause) => void;
165
156
  cancelValidateAsync: () => void;
166
157
  validateAsync: (value: TData | undefined, cause: ValidationCause) => Promise<ValidationError[]>;
@@ -169,4 +160,4 @@ declare class FieldApi<_TData, TFormData, Opts extends FieldApiOptions<_TData, T
169
160
  handleBlur: () => void;
170
161
  }
171
162
 
172
- export { DeepKeys, DeepValue, FieldApi, FieldApiOptions, FieldInfo, FieldMeta, FieldOptions, FieldState, FormApi, FormOptions, FormState, Updater, ValidationCause, ValidationError, ValidationErrorMap, ValidationErrorMapKeys, ValidationMeta };
163
+ export { DeepKeys, DeepValue, FieldApi, FieldApiOptions, FieldInfo, FieldMeta, FieldOptions, FieldState, FormApi, FormOptions, FormState, ResolveName, Updater, ValidationCause, ValidationError, ValidationErrorMap, ValidationErrorMapKeys, ValidationMeta };
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/utils.ts"],"sourcesContent":["export type UpdaterFn<TInput, TOutput = TInput> = (input: TInput) => TOutput\n\nexport type Updater<TInput, TOutput = TInput> =\n | TOutput\n | UpdaterFn<TInput, TOutput>\n\nexport function functionalUpdate<TInput, TOutput = TInput>(\n updater: Updater<TInput, TOutput>,\n input: TInput,\n): TOutput {\n return typeof updater === 'function'\n ? (updater as UpdaterFn<TInput, TOutput>)(input)\n : updater\n}\n\n/**\n * Get a value from an object using a path, including dot notation.\n */\nexport function getBy(obj: any, path: any) {\n const pathArray = makePathArray(path)\n const pathObj = pathArray\n return pathObj.reduce((current: any, pathPart: any) => {\n if (typeof current !== 'undefined') {\n return current[pathPart]\n }\n return undefined\n }, obj)\n}\n\n/**\n * Set a value on an object using a path, including dot notation.\n */\nexport function setBy(obj: any, _path: any, updater: Updater<any>) {\n const path = makePathArray(_path)\n\n function doSet(parent?: any): any {\n if (!path.length) {\n return functionalUpdate(updater, parent)\n }\n\n const key = path.shift()\n\n if (typeof key === 'string') {\n if (typeof parent === 'object') {\n return {\n ...parent,\n [key]: doSet(parent[key]),\n }\n }\n return {\n [key]: doSet(),\n }\n }\n\n if (typeof key === 'number') {\n if (Array.isArray(parent)) {\n const prefix = parent.slice(0, key)\n return [\n ...(prefix.length ? prefix : new Array(key)),\n doSet(parent[key]),\n ...parent.slice(key + 1),\n ]\n }\n return [...new Array(key), doSet()]\n }\n\n throw new Error('Uh oh!')\n }\n\n return doSet(obj)\n}\n\nconst reFindNumbers0 = /^(\\d*)$/gm\nconst reFindNumbers1 = /\\.(\\d*)\\./gm\nconst reFindNumbers2 = /^(\\d*)\\./gm\nconst reFindNumbers3 = /\\.(\\d*$)/gm\nconst reFindMultiplePeriods = /\\.{2,}/gm\n\nconst intPrefix = '__int__'\nconst intReplace = `${intPrefix}$1`\n\nfunction makePathArray(str: string) {\n if (typeof str !== 'string') {\n throw new Error('Path must be a string.')\n }\n\n return str\n .replace('[', '.')\n .replace(']', '')\n .replace(reFindNumbers0, intReplace)\n .replace(reFindNumbers1, `.${intReplace}.`)\n .replace(reFindNumbers2, `${intReplace}.`)\n .replace(reFindNumbers3, `.${intReplace}`)\n .replace(reFindMultiplePeriods, '.')\n .split('.')\n .map((d) => {\n if (d.indexOf(intPrefix) === 0) {\n return parseInt(d.substring(intPrefix.length), 10)\n }\n return d\n })\n}\n\nexport function isNonEmptyArray(obj: any) {\n return !(Array.isArray(obj) && obj.length === 0)\n}\n\nexport type RequiredByKey<T, K extends keyof T> = Omit<T, K> &\n Required<Pick<T, K>>\n\ntype ComputeRange<\n N extends number,\n Result extends Array<unknown> = [],\n> = Result['length'] extends N\n ? Result\n : ComputeRange<N, [...Result, Result['length']]>\ntype Index40 = ComputeRange<40>[number]\n\n// Is this type a tuple?\ntype IsTuple<T> = T extends readonly any[] & { length: infer Length }\n ? Length extends Index40\n ? T\n : never\n : never\n\n// If this type is a tuple, what indices are allowed?\ntype AllowedIndexes<\n Tuple extends ReadonlyArray<any>,\n Keys extends number = never,\n> = Tuple extends readonly []\n ? Keys\n : Tuple extends readonly [infer _, ...infer Tail]\n ? AllowedIndexes<Tail, Keys | Tail['length']>\n : Keys\n\nexport type DeepKeys<T> = unknown extends T\n ? keyof T\n : object extends T\n ? string\n : T extends readonly any[] & IsTuple<T>\n ? AllowedIndexes<T> | DeepKeysPrefix<T, AllowedIndexes<T>>\n : T extends any[]\n ? DeepKeys<T[number]>\n : T extends Date\n ? never\n : T extends object\n ? (keyof T & string) | DeepKeysPrefix<T, keyof T>\n : never\n\ntype DeepKeysPrefix<T, TPrefix> = TPrefix extends keyof T & (number | string)\n ? `${TPrefix}.${DeepKeys<T[TPrefix]> & string}`\n : never\n\nexport type DeepValue<T, TProp> = T extends Record<string | number, any>\n ? TProp extends `${infer TBranch}.${infer TDeepProp}`\n ? DeepValue<T[TBranch], TDeepProp>\n : T[TProp & string]\n : never\n\ntype Narrowable = string | number | bigint | boolean\n\ntype NarrowRaw<A> =\n | (A extends [] ? [] : never)\n | (A extends Narrowable ? A : never)\n | {\n [K in keyof A]: A[K] extends Function ? A[K] : NarrowRaw<A[K]>\n }\n\nexport type Narrow<A> = Try<A, [], NarrowRaw<A>>\n\ntype Try<A1, A2, Catch = never> = A1 extends A2 ? A1 : Catch\n\n// Hack to get TypeScript to show simplified types in error messages\nexport type Pretty<T> = { [K in keyof T]: T[K] } & {}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAMO,SAAS,iBACd,SACA,OACS;AACT,SAAO,OAAO,YAAY,aACrB,QAAuC,KAAK,IAC7C;AACN;AAKO,SAAS,MAAM,KAAU,MAAW;AACzC,QAAM,YAAY,cAAc,IAAI;AACpC,QAAM,UAAU;AAChB,SAAO,QAAQ,OAAO,CAAC,SAAc,aAAkB;AACrD,QAAI,OAAO,YAAY,aAAa;AAClC,aAAO,QAAQ,QAAQ;AAAA,IACzB;AACA,WAAO;AAAA,EACT,GAAG,GAAG;AACR;AAKO,SAAS,MAAM,KAAU,OAAY,SAAuB;AACjE,QAAM,OAAO,cAAc,KAAK;AAEhC,WAAS,MAAM,QAAmB;AAChC,QAAI,CAAC,KAAK,QAAQ;AAChB,aAAO,iBAAiB,SAAS,MAAM;AAAA,IACzC;AAEA,UAAM,MAAM,KAAK,MAAM;AAEvB,QAAI,OAAO,QAAQ,UAAU;AAC3B,UAAI,OAAO,WAAW,UAAU;AAC9B,eAAO;AAAA,UACL,GAAG;AAAA,UACH,CAAC,GAAG,GAAG,MAAM,OAAO,GAAG,CAAC;AAAA,QAC1B;AAAA,MACF;AACA,aAAO;AAAA,QACL,CAAC,GAAG,GAAG,MAAM;AAAA,MACf;AAAA,IACF;AAEA,QAAI,OAAO,QAAQ,UAAU;AAC3B,UAAI,MAAM,QAAQ,MAAM,GAAG;AACzB,cAAM,SAAS,OAAO,MAAM,GAAG,GAAG;AAClC,eAAO;AAAA,UACL,GAAI,OAAO,SAAS,SAAS,IAAI,MAAM,GAAG;AAAA,UAC1C,MAAM,OAAO,GAAG,CAAC;AAAA,UACjB,GAAG,OAAO,MAAM,MAAM,CAAC;AAAA,QACzB;AAAA,MACF;AACA,aAAO,CAAC,GAAG,IAAI,MAAM,GAAG,GAAG,MAAM,CAAC;AAAA,IACpC;AAEA,UAAM,IAAI,MAAM,QAAQ;AAAA,EAC1B;AAEA,SAAO,MAAM,GAAG;AAClB;AAEA,IAAM,iBAAiB;AACvB,IAAM,iBAAiB;AACvB,IAAM,iBAAiB;AACvB,IAAM,iBAAiB;AACvB,IAAM,wBAAwB;AAE9B,IAAM,YAAY;AAClB,IAAM,aAAa,GAAG,SAAS;AAE/B,SAAS,cAAc,KAAa;AAClC,MAAI,OAAO,QAAQ,UAAU;AAC3B,UAAM,IAAI,MAAM,wBAAwB;AAAA,EAC1C;AAEA,SAAO,IACJ,QAAQ,KAAK,GAAG,EAChB,QAAQ,KAAK,EAAE,EACf,QAAQ,gBAAgB,UAAU,EAClC,QAAQ,gBAAgB,IAAI,UAAU,GAAG,EACzC,QAAQ,gBAAgB,GAAG,UAAU,GAAG,EACxC,QAAQ,gBAAgB,IAAI,UAAU,EAAE,EACxC,QAAQ,uBAAuB,GAAG,EAClC,MAAM,GAAG,EACT,IAAI,CAAC,MAAM;AACV,QAAI,EAAE,QAAQ,SAAS,MAAM,GAAG;AAC9B,aAAO,SAAS,EAAE,UAAU,UAAU,MAAM,GAAG,EAAE;AAAA,IACnD;AACA,WAAO;AAAA,EACT,CAAC;AACL;AAEO,SAAS,gBAAgB,KAAU;AACxC,SAAO,EAAE,MAAM,QAAQ,GAAG,KAAK,IAAI,WAAW;AAChD;","names":[]}
1
+ {"version":3,"sources":["../../src/utils.ts"],"sourcesContent":["export type UpdaterFn<TInput, TOutput = TInput> = (input: TInput) => TOutput\n\nexport type Updater<TInput, TOutput = TInput> =\n | TOutput\n | UpdaterFn<TInput, TOutput>\n\nexport function functionalUpdate<TInput, TOutput = TInput>(\n updater: Updater<TInput, TOutput>,\n input: TInput,\n): TOutput {\n return typeof updater === 'function'\n ? (updater as UpdaterFn<TInput, TOutput>)(input)\n : updater\n}\n\n/**\n * Get a value from an object using a path, including dot notation.\n */\nexport function getBy(obj: any, path: any) {\n const pathArray = makePathArray(path)\n const pathObj = pathArray\n return pathObj.reduce((current: any, pathPart: any) => {\n if (typeof current !== 'undefined') {\n return current[pathPart]\n }\n return undefined\n }, obj)\n}\n\n/**\n * Set a value on an object using a path, including dot notation.\n */\nexport function setBy(obj: any, _path: any, updater: Updater<any>) {\n const path = makePathArray(_path)\n\n function doSet(parent?: any): any {\n if (!path.length) {\n return functionalUpdate(updater, parent)\n }\n\n const key = path.shift()\n\n if (typeof key === 'string') {\n if (typeof parent === 'object') {\n return {\n ...parent,\n [key]: doSet(parent[key]),\n }\n }\n return {\n [key]: doSet(),\n }\n }\n\n if (typeof key === 'number') {\n if (Array.isArray(parent)) {\n const prefix = parent.slice(0, key)\n return [\n ...(prefix.length ? prefix : new Array(key)),\n doSet(parent[key]),\n ...parent.slice(key + 1),\n ]\n }\n return [...new Array(key), doSet()]\n }\n\n throw new Error('Uh oh!')\n }\n\n return doSet(obj)\n}\n\nconst reFindNumbers0 = /^(\\d*)$/gm\nconst reFindNumbers1 = /\\.(\\d*)\\./gm\nconst reFindNumbers2 = /^(\\d*)\\./gm\nconst reFindNumbers3 = /\\.(\\d*$)/gm\nconst reFindMultiplePeriods = /\\.{2,}/gm\n\nconst intPrefix = '__int__'\nconst intReplace = `${intPrefix}$1`\n\nfunction makePathArray(str: string) {\n if (typeof str !== 'string') {\n throw new Error('Path must be a string.')\n }\n\n return str\n .replace('[', '.')\n .replace(']', '')\n .replace(reFindNumbers0, intReplace)\n .replace(reFindNumbers1, `.${intReplace}.`)\n .replace(reFindNumbers2, `${intReplace}.`)\n .replace(reFindNumbers3, `.${intReplace}`)\n .replace(reFindMultiplePeriods, '.')\n .split('.')\n .map((d) => {\n if (d.indexOf(intPrefix) === 0) {\n return parseInt(d.substring(intPrefix.length), 10)\n }\n return d\n })\n}\n\nexport function isNonEmptyArray(obj: any) {\n return !(Array.isArray(obj) && obj.length === 0)\n}\n\nexport type RequiredByKey<T, K extends keyof T> = Omit<T, K> &\n Required<Pick<T, K>>\n\ntype ComputeRange<\n N extends number,\n Result extends Array<unknown> = [],\n> = Result['length'] extends N\n ? Result\n : ComputeRange<N, [...Result, Result['length']]>\ntype Index40 = ComputeRange<40>[number]\n\n// Is this type a tuple?\ntype IsTuple<T> = T extends readonly any[] & { length: infer Length }\n ? Length extends Index40\n ? T\n : never\n : never\n\n// If this type is a tuple, what indices are allowed?\ntype AllowedIndexes<\n Tuple extends ReadonlyArray<any>,\n Keys extends number = never,\n> = Tuple extends readonly []\n ? Keys\n : Tuple extends readonly [infer _, ...infer Tail]\n ? AllowedIndexes<Tail, Keys | Tail['length']>\n : Keys\n\nexport type DeepKeys<T, TDepth extends any[] = []> = TDepth['length'] extends 5\n ? never\n : unknown extends T\n ? string\n : object extends T\n ? string\n : T extends readonly any[] & IsTuple<T>\n ? AllowedIndexes<T> | DeepKeysPrefix<T, AllowedIndexes<T>, TDepth>\n : T extends any[]\n ? DeepKeys<T[number], [...TDepth, any]>\n : T extends Date\n ? never\n : T extends object\n ? (keyof T & string) | DeepKeysPrefix<T, keyof T, TDepth>\n : never\n\ntype DeepKeysPrefix<\n T,\n TPrefix,\n TDepth extends any[],\n> = TPrefix extends keyof T & (number | string)\n ? `${TPrefix}.${DeepKeys<T[TPrefix], [...TDepth, any]> & string}`\n : never\n\nexport type DeepValue<T, TProp> = T extends Record<string | number, any>\n ? TProp extends `${infer TBranch}.${infer TDeepProp}`\n ? DeepValue<T[TBranch], TDeepProp>\n : T[TProp & string]\n : never\n\ntype Narrowable = string | number | bigint | boolean\n\ntype NarrowRaw<A> =\n | (A extends [] ? [] : never)\n | (A extends Narrowable ? A : never)\n | {\n [K in keyof A]: A[K] extends Function ? A[K] : NarrowRaw<A[K]>\n }\n\nexport type Narrow<A> = Try<A, [], NarrowRaw<A>>\n\ntype Try<A1, A2, Catch = never> = A1 extends A2 ? A1 : Catch\n\n// Hack to get TypeScript to show simplified types in error messages\nexport type Pretty<T> = { [K in keyof T]: T[K] } & {}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAMO,SAAS,iBACd,SACA,OACS;AACT,SAAO,OAAO,YAAY,aACrB,QAAuC,KAAK,IAC7C;AACN;AAKO,SAAS,MAAM,KAAU,MAAW;AACzC,QAAM,YAAY,cAAc,IAAI;AACpC,QAAM,UAAU;AAChB,SAAO,QAAQ,OAAO,CAAC,SAAc,aAAkB;AACrD,QAAI,OAAO,YAAY,aAAa;AAClC,aAAO,QAAQ,QAAQ;AAAA,IACzB;AACA,WAAO;AAAA,EACT,GAAG,GAAG;AACR;AAKO,SAAS,MAAM,KAAU,OAAY,SAAuB;AACjE,QAAM,OAAO,cAAc,KAAK;AAEhC,WAAS,MAAM,QAAmB;AAChC,QAAI,CAAC,KAAK,QAAQ;AAChB,aAAO,iBAAiB,SAAS,MAAM;AAAA,IACzC;AAEA,UAAM,MAAM,KAAK,MAAM;AAEvB,QAAI,OAAO,QAAQ,UAAU;AAC3B,UAAI,OAAO,WAAW,UAAU;AAC9B,eAAO;AAAA,UACL,GAAG;AAAA,UACH,CAAC,GAAG,GAAG,MAAM,OAAO,GAAG,CAAC;AAAA,QAC1B;AAAA,MACF;AACA,aAAO;AAAA,QACL,CAAC,GAAG,GAAG,MAAM;AAAA,MACf;AAAA,IACF;AAEA,QAAI,OAAO,QAAQ,UAAU;AAC3B,UAAI,MAAM,QAAQ,MAAM,GAAG;AACzB,cAAM,SAAS,OAAO,MAAM,GAAG,GAAG;AAClC,eAAO;AAAA,UACL,GAAI,OAAO,SAAS,SAAS,IAAI,MAAM,GAAG;AAAA,UAC1C,MAAM,OAAO,GAAG,CAAC;AAAA,UACjB,GAAG,OAAO,MAAM,MAAM,CAAC;AAAA,QACzB;AAAA,MACF;AACA,aAAO,CAAC,GAAG,IAAI,MAAM,GAAG,GAAG,MAAM,CAAC;AAAA,IACpC;AAEA,UAAM,IAAI,MAAM,QAAQ;AAAA,EAC1B;AAEA,SAAO,MAAM,GAAG;AAClB;AAEA,IAAM,iBAAiB;AACvB,IAAM,iBAAiB;AACvB,IAAM,iBAAiB;AACvB,IAAM,iBAAiB;AACvB,IAAM,wBAAwB;AAE9B,IAAM,YAAY;AAClB,IAAM,aAAa,GAAG,SAAS;AAE/B,SAAS,cAAc,KAAa;AAClC,MAAI,OAAO,QAAQ,UAAU;AAC3B,UAAM,IAAI,MAAM,wBAAwB;AAAA,EAC1C;AAEA,SAAO,IACJ,QAAQ,KAAK,GAAG,EAChB,QAAQ,KAAK,EAAE,EACf,QAAQ,gBAAgB,UAAU,EAClC,QAAQ,gBAAgB,IAAI,UAAU,GAAG,EACzC,QAAQ,gBAAgB,GAAG,UAAU,GAAG,EACxC,QAAQ,gBAAgB,IAAI,UAAU,EAAE,EACxC,QAAQ,uBAAuB,GAAG,EAClC,MAAM,GAAG,EACT,IAAI,CAAC,MAAM;AACV,QAAI,EAAE,QAAQ,SAAS,MAAM,GAAG;AAC9B,aAAO,SAAS,EAAE,UAAU,UAAU,MAAM,GAAG,EAAE;AAAA,IACnD;AACA,WAAO;AAAA,EACT,CAAC;AACL;AAEO,SAAS,gBAAgB,KAAU;AACxC,SAAO,EAAE,MAAM,QAAQ,GAAG,KAAK,IAAI,WAAW;AAChD;","names":[]}
@@ -17,8 +17,8 @@ type IsTuple<T> = T extends readonly any[] & {
17
17
  length: infer Length;
18
18
  } ? Length extends Index40 ? T : never : never;
19
19
  type AllowedIndexes<Tuple extends ReadonlyArray<any>, Keys extends number = never> = Tuple extends readonly [] ? Keys : Tuple extends readonly [infer _, ...infer Tail] ? AllowedIndexes<Tail, Keys | Tail['length']> : Keys;
20
- type DeepKeys<T> = unknown extends T ? keyof T : object extends T ? string : T extends readonly any[] & IsTuple<T> ? AllowedIndexes<T> | DeepKeysPrefix<T, AllowedIndexes<T>> : T extends any[] ? DeepKeys<T[number]> : T extends Date ? never : T extends object ? (keyof T & string) | DeepKeysPrefix<T, keyof T> : never;
21
- type DeepKeysPrefix<T, TPrefix> = TPrefix extends keyof T & (number | string) ? `${TPrefix}.${DeepKeys<T[TPrefix]> & string}` : never;
20
+ type DeepKeys<T, TDepth extends any[] = []> = TDepth['length'] extends 5 ? never : unknown extends T ? string : object extends T ? string : T extends readonly any[] & IsTuple<T> ? AllowedIndexes<T> | DeepKeysPrefix<T, AllowedIndexes<T>, TDepth> : T extends any[] ? DeepKeys<T[number], [...TDepth, any]> : T extends Date ? never : T extends object ? (keyof T & string) | DeepKeysPrefix<T, keyof T, TDepth> : never;
21
+ type DeepKeysPrefix<T, TPrefix, TDepth extends any[]> = TPrefix extends keyof T & (number | string) ? `${TPrefix}.${DeepKeys<T[TPrefix], [...TDepth, any]> & string}` : never;
22
22
  type DeepValue<T, TProp> = T extends Record<string | number, any> ? TProp extends `${infer TBranch}.${infer TDeepProp}` ? DeepValue<T[TBranch], TDeepProp> : T[TProp & string] : never;
23
23
  type Narrowable = string | number | bigint | boolean;
24
24
  type NarrowRaw<A> = (A extends [] ? [] : never) | (A extends Narrowable ? A : never) | {
@@ -17,8 +17,8 @@ type IsTuple<T> = T extends readonly any[] & {
17
17
  length: infer Length;
18
18
  } ? Length extends Index40 ? T : never : never;
19
19
  type AllowedIndexes<Tuple extends ReadonlyArray<any>, Keys extends number = never> = Tuple extends readonly [] ? Keys : Tuple extends readonly [infer _, ...infer Tail] ? AllowedIndexes<Tail, Keys | Tail['length']> : Keys;
20
- type DeepKeys<T> = unknown extends T ? keyof T : object extends T ? string : T extends readonly any[] & IsTuple<T> ? AllowedIndexes<T> | DeepKeysPrefix<T, AllowedIndexes<T>> : T extends any[] ? DeepKeys<T[number]> : T extends Date ? never : T extends object ? (keyof T & string) | DeepKeysPrefix<T, keyof T> : never;
21
- type DeepKeysPrefix<T, TPrefix> = TPrefix extends keyof T & (number | string) ? `${TPrefix}.${DeepKeys<T[TPrefix]> & string}` : never;
20
+ type DeepKeys<T, TDepth extends any[] = []> = TDepth['length'] extends 5 ? never : unknown extends T ? string : object extends T ? string : T extends readonly any[] & IsTuple<T> ? AllowedIndexes<T> | DeepKeysPrefix<T, AllowedIndexes<T>, TDepth> : T extends any[] ? DeepKeys<T[number], [...TDepth, any]> : T extends Date ? never : T extends object ? (keyof T & string) | DeepKeysPrefix<T, keyof T, TDepth> : never;
21
+ type DeepKeysPrefix<T, TPrefix, TDepth extends any[]> = TPrefix extends keyof T & (number | string) ? `${TPrefix}.${DeepKeys<T[TPrefix], [...TDepth, any]> & string}` : never;
22
22
  type DeepValue<T, TProp> = T extends Record<string | number, any> ? TProp extends `${infer TBranch}.${infer TDeepProp}` ? DeepValue<T[TBranch], TDeepProp> : T[TProp & string] : never;
23
23
  type Narrowable = string | number | bigint | boolean;
24
24
  type NarrowRaw<A> = (A extends [] ? [] : never) | (A extends Narrowable ? A : never) | {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/utils.ts"],"sourcesContent":["export type UpdaterFn<TInput, TOutput = TInput> = (input: TInput) => TOutput\n\nexport type Updater<TInput, TOutput = TInput> =\n | TOutput\n | UpdaterFn<TInput, TOutput>\n\nexport function functionalUpdate<TInput, TOutput = TInput>(\n updater: Updater<TInput, TOutput>,\n input: TInput,\n): TOutput {\n return typeof updater === 'function'\n ? (updater as UpdaterFn<TInput, TOutput>)(input)\n : updater\n}\n\n/**\n * Get a value from an object using a path, including dot notation.\n */\nexport function getBy(obj: any, path: any) {\n const pathArray = makePathArray(path)\n const pathObj = pathArray\n return pathObj.reduce((current: any, pathPart: any) => {\n if (typeof current !== 'undefined') {\n return current[pathPart]\n }\n return undefined\n }, obj)\n}\n\n/**\n * Set a value on an object using a path, including dot notation.\n */\nexport function setBy(obj: any, _path: any, updater: Updater<any>) {\n const path = makePathArray(_path)\n\n function doSet(parent?: any): any {\n if (!path.length) {\n return functionalUpdate(updater, parent)\n }\n\n const key = path.shift()\n\n if (typeof key === 'string') {\n if (typeof parent === 'object') {\n return {\n ...parent,\n [key]: doSet(parent[key]),\n }\n }\n return {\n [key]: doSet(),\n }\n }\n\n if (typeof key === 'number') {\n if (Array.isArray(parent)) {\n const prefix = parent.slice(0, key)\n return [\n ...(prefix.length ? prefix : new Array(key)),\n doSet(parent[key]),\n ...parent.slice(key + 1),\n ]\n }\n return [...new Array(key), doSet()]\n }\n\n throw new Error('Uh oh!')\n }\n\n return doSet(obj)\n}\n\nconst reFindNumbers0 = /^(\\d*)$/gm\nconst reFindNumbers1 = /\\.(\\d*)\\./gm\nconst reFindNumbers2 = /^(\\d*)\\./gm\nconst reFindNumbers3 = /\\.(\\d*$)/gm\nconst reFindMultiplePeriods = /\\.{2,}/gm\n\nconst intPrefix = '__int__'\nconst intReplace = `${intPrefix}$1`\n\nfunction makePathArray(str: string) {\n if (typeof str !== 'string') {\n throw new Error('Path must be a string.')\n }\n\n return str\n .replace('[', '.')\n .replace(']', '')\n .replace(reFindNumbers0, intReplace)\n .replace(reFindNumbers1, `.${intReplace}.`)\n .replace(reFindNumbers2, `${intReplace}.`)\n .replace(reFindNumbers3, `.${intReplace}`)\n .replace(reFindMultiplePeriods, '.')\n .split('.')\n .map((d) => {\n if (d.indexOf(intPrefix) === 0) {\n return parseInt(d.substring(intPrefix.length), 10)\n }\n return d\n })\n}\n\nexport function isNonEmptyArray(obj: any) {\n return !(Array.isArray(obj) && obj.length === 0)\n}\n\nexport type RequiredByKey<T, K extends keyof T> = Omit<T, K> &\n Required<Pick<T, K>>\n\ntype ComputeRange<\n N extends number,\n Result extends Array<unknown> = [],\n> = Result['length'] extends N\n ? Result\n : ComputeRange<N, [...Result, Result['length']]>\ntype Index40 = ComputeRange<40>[number]\n\n// Is this type a tuple?\ntype IsTuple<T> = T extends readonly any[] & { length: infer Length }\n ? Length extends Index40\n ? T\n : never\n : never\n\n// If this type is a tuple, what indices are allowed?\ntype AllowedIndexes<\n Tuple extends ReadonlyArray<any>,\n Keys extends number = never,\n> = Tuple extends readonly []\n ? Keys\n : Tuple extends readonly [infer _, ...infer Tail]\n ? AllowedIndexes<Tail, Keys | Tail['length']>\n : Keys\n\nexport type DeepKeys<T> = unknown extends T\n ? keyof T\n : object extends T\n ? string\n : T extends readonly any[] & IsTuple<T>\n ? AllowedIndexes<T> | DeepKeysPrefix<T, AllowedIndexes<T>>\n : T extends any[]\n ? DeepKeys<T[number]>\n : T extends Date\n ? never\n : T extends object\n ? (keyof T & string) | DeepKeysPrefix<T, keyof T>\n : never\n\ntype DeepKeysPrefix<T, TPrefix> = TPrefix extends keyof T & (number | string)\n ? `${TPrefix}.${DeepKeys<T[TPrefix]> & string}`\n : never\n\nexport type DeepValue<T, TProp> = T extends Record<string | number, any>\n ? TProp extends `${infer TBranch}.${infer TDeepProp}`\n ? DeepValue<T[TBranch], TDeepProp>\n : T[TProp & string]\n : never\n\ntype Narrowable = string | number | bigint | boolean\n\ntype NarrowRaw<A> =\n | (A extends [] ? [] : never)\n | (A extends Narrowable ? A : never)\n | {\n [K in keyof A]: A[K] extends Function ? A[K] : NarrowRaw<A[K]>\n }\n\nexport type Narrow<A> = Try<A, [], NarrowRaw<A>>\n\ntype Try<A1, A2, Catch = never> = A1 extends A2 ? A1 : Catch\n\n// Hack to get TypeScript to show simplified types in error messages\nexport type Pretty<T> = { [K in keyof T]: T[K] } & {}\n"],"mappings":";AAMO,SAAS,iBACd,SACA,OACS;AACT,SAAO,OAAO,YAAY,aACrB,QAAuC,KAAK,IAC7C;AACN;AAKO,SAAS,MAAM,KAAU,MAAW;AACzC,QAAM,YAAY,cAAc,IAAI;AACpC,QAAM,UAAU;AAChB,SAAO,QAAQ,OAAO,CAAC,SAAc,aAAkB;AACrD,QAAI,OAAO,YAAY,aAAa;AAClC,aAAO,QAAQ,QAAQ;AAAA,IACzB;AACA,WAAO;AAAA,EACT,GAAG,GAAG;AACR;AAKO,SAAS,MAAM,KAAU,OAAY,SAAuB;AACjE,QAAM,OAAO,cAAc,KAAK;AAEhC,WAAS,MAAM,QAAmB;AAChC,QAAI,CAAC,KAAK,QAAQ;AAChB,aAAO,iBAAiB,SAAS,MAAM;AAAA,IACzC;AAEA,UAAM,MAAM,KAAK,MAAM;AAEvB,QAAI,OAAO,QAAQ,UAAU;AAC3B,UAAI,OAAO,WAAW,UAAU;AAC9B,eAAO;AAAA,UACL,GAAG;AAAA,UACH,CAAC,GAAG,GAAG,MAAM,OAAO,GAAG,CAAC;AAAA,QAC1B;AAAA,MACF;AACA,aAAO;AAAA,QACL,CAAC,GAAG,GAAG,MAAM;AAAA,MACf;AAAA,IACF;AAEA,QAAI,OAAO,QAAQ,UAAU;AAC3B,UAAI,MAAM,QAAQ,MAAM,GAAG;AACzB,cAAM,SAAS,OAAO,MAAM,GAAG,GAAG;AAClC,eAAO;AAAA,UACL,GAAI,OAAO,SAAS,SAAS,IAAI,MAAM,GAAG;AAAA,UAC1C,MAAM,OAAO,GAAG,CAAC;AAAA,UACjB,GAAG,OAAO,MAAM,MAAM,CAAC;AAAA,QACzB;AAAA,MACF;AACA,aAAO,CAAC,GAAG,IAAI,MAAM,GAAG,GAAG,MAAM,CAAC;AAAA,IACpC;AAEA,UAAM,IAAI,MAAM,QAAQ;AAAA,EAC1B;AAEA,SAAO,MAAM,GAAG;AAClB;AAEA,IAAM,iBAAiB;AACvB,IAAM,iBAAiB;AACvB,IAAM,iBAAiB;AACvB,IAAM,iBAAiB;AACvB,IAAM,wBAAwB;AAE9B,IAAM,YAAY;AAClB,IAAM,aAAa,GAAG,SAAS;AAE/B,SAAS,cAAc,KAAa;AAClC,MAAI,OAAO,QAAQ,UAAU;AAC3B,UAAM,IAAI,MAAM,wBAAwB;AAAA,EAC1C;AAEA,SAAO,IACJ,QAAQ,KAAK,GAAG,EAChB,QAAQ,KAAK,EAAE,EACf,QAAQ,gBAAgB,UAAU,EAClC,QAAQ,gBAAgB,IAAI,UAAU,GAAG,EACzC,QAAQ,gBAAgB,GAAG,UAAU,GAAG,EACxC,QAAQ,gBAAgB,IAAI,UAAU,EAAE,EACxC,QAAQ,uBAAuB,GAAG,EAClC,MAAM,GAAG,EACT,IAAI,CAAC,MAAM;AACV,QAAI,EAAE,QAAQ,SAAS,MAAM,GAAG;AAC9B,aAAO,SAAS,EAAE,UAAU,UAAU,MAAM,GAAG,EAAE;AAAA,IACnD;AACA,WAAO;AAAA,EACT,CAAC;AACL;AAEO,SAAS,gBAAgB,KAAU;AACxC,SAAO,EAAE,MAAM,QAAQ,GAAG,KAAK,IAAI,WAAW;AAChD;","names":[]}
1
+ {"version":3,"sources":["../../src/utils.ts"],"sourcesContent":["export type UpdaterFn<TInput, TOutput = TInput> = (input: TInput) => TOutput\n\nexport type Updater<TInput, TOutput = TInput> =\n | TOutput\n | UpdaterFn<TInput, TOutput>\n\nexport function functionalUpdate<TInput, TOutput = TInput>(\n updater: Updater<TInput, TOutput>,\n input: TInput,\n): TOutput {\n return typeof updater === 'function'\n ? (updater as UpdaterFn<TInput, TOutput>)(input)\n : updater\n}\n\n/**\n * Get a value from an object using a path, including dot notation.\n */\nexport function getBy(obj: any, path: any) {\n const pathArray = makePathArray(path)\n const pathObj = pathArray\n return pathObj.reduce((current: any, pathPart: any) => {\n if (typeof current !== 'undefined') {\n return current[pathPart]\n }\n return undefined\n }, obj)\n}\n\n/**\n * Set a value on an object using a path, including dot notation.\n */\nexport function setBy(obj: any, _path: any, updater: Updater<any>) {\n const path = makePathArray(_path)\n\n function doSet(parent?: any): any {\n if (!path.length) {\n return functionalUpdate(updater, parent)\n }\n\n const key = path.shift()\n\n if (typeof key === 'string') {\n if (typeof parent === 'object') {\n return {\n ...parent,\n [key]: doSet(parent[key]),\n }\n }\n return {\n [key]: doSet(),\n }\n }\n\n if (typeof key === 'number') {\n if (Array.isArray(parent)) {\n const prefix = parent.slice(0, key)\n return [\n ...(prefix.length ? prefix : new Array(key)),\n doSet(parent[key]),\n ...parent.slice(key + 1),\n ]\n }\n return [...new Array(key), doSet()]\n }\n\n throw new Error('Uh oh!')\n }\n\n return doSet(obj)\n}\n\nconst reFindNumbers0 = /^(\\d*)$/gm\nconst reFindNumbers1 = /\\.(\\d*)\\./gm\nconst reFindNumbers2 = /^(\\d*)\\./gm\nconst reFindNumbers3 = /\\.(\\d*$)/gm\nconst reFindMultiplePeriods = /\\.{2,}/gm\n\nconst intPrefix = '__int__'\nconst intReplace = `${intPrefix}$1`\n\nfunction makePathArray(str: string) {\n if (typeof str !== 'string') {\n throw new Error('Path must be a string.')\n }\n\n return str\n .replace('[', '.')\n .replace(']', '')\n .replace(reFindNumbers0, intReplace)\n .replace(reFindNumbers1, `.${intReplace}.`)\n .replace(reFindNumbers2, `${intReplace}.`)\n .replace(reFindNumbers3, `.${intReplace}`)\n .replace(reFindMultiplePeriods, '.')\n .split('.')\n .map((d) => {\n if (d.indexOf(intPrefix) === 0) {\n return parseInt(d.substring(intPrefix.length), 10)\n }\n return d\n })\n}\n\nexport function isNonEmptyArray(obj: any) {\n return !(Array.isArray(obj) && obj.length === 0)\n}\n\nexport type RequiredByKey<T, K extends keyof T> = Omit<T, K> &\n Required<Pick<T, K>>\n\ntype ComputeRange<\n N extends number,\n Result extends Array<unknown> = [],\n> = Result['length'] extends N\n ? Result\n : ComputeRange<N, [...Result, Result['length']]>\ntype Index40 = ComputeRange<40>[number]\n\n// Is this type a tuple?\ntype IsTuple<T> = T extends readonly any[] & { length: infer Length }\n ? Length extends Index40\n ? T\n : never\n : never\n\n// If this type is a tuple, what indices are allowed?\ntype AllowedIndexes<\n Tuple extends ReadonlyArray<any>,\n Keys extends number = never,\n> = Tuple extends readonly []\n ? Keys\n : Tuple extends readonly [infer _, ...infer Tail]\n ? AllowedIndexes<Tail, Keys | Tail['length']>\n : Keys\n\nexport type DeepKeys<T, TDepth extends any[] = []> = TDepth['length'] extends 5\n ? never\n : unknown extends T\n ? string\n : object extends T\n ? string\n : T extends readonly any[] & IsTuple<T>\n ? AllowedIndexes<T> | DeepKeysPrefix<T, AllowedIndexes<T>, TDepth>\n : T extends any[]\n ? DeepKeys<T[number], [...TDepth, any]>\n : T extends Date\n ? never\n : T extends object\n ? (keyof T & string) | DeepKeysPrefix<T, keyof T, TDepth>\n : never\n\ntype DeepKeysPrefix<\n T,\n TPrefix,\n TDepth extends any[],\n> = TPrefix extends keyof T & (number | string)\n ? `${TPrefix}.${DeepKeys<T[TPrefix], [...TDepth, any]> & string}`\n : never\n\nexport type DeepValue<T, TProp> = T extends Record<string | number, any>\n ? TProp extends `${infer TBranch}.${infer TDeepProp}`\n ? DeepValue<T[TBranch], TDeepProp>\n : T[TProp & string]\n : never\n\ntype Narrowable = string | number | bigint | boolean\n\ntype NarrowRaw<A> =\n | (A extends [] ? [] : never)\n | (A extends Narrowable ? A : never)\n | {\n [K in keyof A]: A[K] extends Function ? A[K] : NarrowRaw<A[K]>\n }\n\nexport type Narrow<A> = Try<A, [], NarrowRaw<A>>\n\ntype Try<A1, A2, Catch = never> = A1 extends A2 ? A1 : Catch\n\n// Hack to get TypeScript to show simplified types in error messages\nexport type Pretty<T> = { [K in keyof T]: T[K] } & {}\n"],"mappings":";AAMO,SAAS,iBACd,SACA,OACS;AACT,SAAO,OAAO,YAAY,aACrB,QAAuC,KAAK,IAC7C;AACN;AAKO,SAAS,MAAM,KAAU,MAAW;AACzC,QAAM,YAAY,cAAc,IAAI;AACpC,QAAM,UAAU;AAChB,SAAO,QAAQ,OAAO,CAAC,SAAc,aAAkB;AACrD,QAAI,OAAO,YAAY,aAAa;AAClC,aAAO,QAAQ,QAAQ;AAAA,IACzB;AACA,WAAO;AAAA,EACT,GAAG,GAAG;AACR;AAKO,SAAS,MAAM,KAAU,OAAY,SAAuB;AACjE,QAAM,OAAO,cAAc,KAAK;AAEhC,WAAS,MAAM,QAAmB;AAChC,QAAI,CAAC,KAAK,QAAQ;AAChB,aAAO,iBAAiB,SAAS,MAAM;AAAA,IACzC;AAEA,UAAM,MAAM,KAAK,MAAM;AAEvB,QAAI,OAAO,QAAQ,UAAU;AAC3B,UAAI,OAAO,WAAW,UAAU;AAC9B,eAAO;AAAA,UACL,GAAG;AAAA,UACH,CAAC,GAAG,GAAG,MAAM,OAAO,GAAG,CAAC;AAAA,QAC1B;AAAA,MACF;AACA,aAAO;AAAA,QACL,CAAC,GAAG,GAAG,MAAM;AAAA,MACf;AAAA,IACF;AAEA,QAAI,OAAO,QAAQ,UAAU;AAC3B,UAAI,MAAM,QAAQ,MAAM,GAAG;AACzB,cAAM,SAAS,OAAO,MAAM,GAAG,GAAG;AAClC,eAAO;AAAA,UACL,GAAI,OAAO,SAAS,SAAS,IAAI,MAAM,GAAG;AAAA,UAC1C,MAAM,OAAO,GAAG,CAAC;AAAA,UACjB,GAAG,OAAO,MAAM,MAAM,CAAC;AAAA,QACzB;AAAA,MACF;AACA,aAAO,CAAC,GAAG,IAAI,MAAM,GAAG,GAAG,MAAM,CAAC;AAAA,IACpC;AAEA,UAAM,IAAI,MAAM,QAAQ;AAAA,EAC1B;AAEA,SAAO,MAAM,GAAG;AAClB;AAEA,IAAM,iBAAiB;AACvB,IAAM,iBAAiB;AACvB,IAAM,iBAAiB;AACvB,IAAM,iBAAiB;AACvB,IAAM,wBAAwB;AAE9B,IAAM,YAAY;AAClB,IAAM,aAAa,GAAG,SAAS;AAE/B,SAAS,cAAc,KAAa;AAClC,MAAI,OAAO,QAAQ,UAAU;AAC3B,UAAM,IAAI,MAAM,wBAAwB;AAAA,EAC1C;AAEA,SAAO,IACJ,QAAQ,KAAK,GAAG,EAChB,QAAQ,KAAK,EAAE,EACf,QAAQ,gBAAgB,UAAU,EAClC,QAAQ,gBAAgB,IAAI,UAAU,GAAG,EACzC,QAAQ,gBAAgB,GAAG,UAAU,GAAG,EACxC,QAAQ,gBAAgB,IAAI,UAAU,EAAE,EACxC,QAAQ,uBAAuB,GAAG,EAClC,MAAM,GAAG,EACT,IAAI,CAAC,MAAM;AACV,QAAI,EAAE,QAAQ,SAAS,MAAM,GAAG;AAC9B,aAAO,SAAS,EAAE,UAAU,UAAU,MAAM,GAAG,EAAE;AAAA,IACnD;AACA,WAAO;AAAA,EACT,CAAC;AACL;AAEO,SAAS,gBAAgB,KAAU;AACxC,SAAO,EAAE,MAAM,QAAQ,GAAG,KAAK,IAAI,WAAW;AAChD;","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tanstack/form-core",
3
- "version": "0.3.4",
3
+ "version": "0.3.6",
4
4
  "description": "Powerful, type-safe, framework agnostic forms.",
5
5
  "author": "tannerlinsley",
6
6
  "license": "MIT",
package/src/FieldApi.ts CHANGED
@@ -4,59 +4,54 @@ import { Store } from '@tanstack/store'
4
4
 
5
5
  export type ValidationCause = 'change' | 'blur' | 'submit' | 'mount'
6
6
 
7
- type ValidateFn<TData, TFormData> = (
7
+ type ValidateFn<TParentData, TName extends DeepKeys<TParentData>, TData> = (
8
8
  value: TData,
9
- fieldApi: FieldApi<TData, TFormData>,
9
+ fieldApi: FieldApi<TParentData, TName>,
10
10
  ) => ValidationError
11
11
 
12
- type ValidateAsyncFn<TData, TFormData> = (
12
+ type ValidateAsyncFn<
13
+ TParentData,
14
+ TName extends DeepKeys<TParentData>,
15
+ TData,
16
+ > = (
13
17
  value: TData,
14
- fieldApi: FieldApi<TData, TFormData>,
18
+ fieldApi: FieldApi<TParentData, TName>,
15
19
  ) => ValidationError | Promise<ValidationError>
16
20
 
17
21
  export interface FieldOptions<
18
- _TData,
19
- TFormData,
22
+ TParentData,
20
23
  /**
21
24
  * This allows us to restrict the name to only be a valid field name while
22
25
  * also assigning it to a generic
23
26
  */
24
- TName = unknown extends TFormData ? string : DeepKeys<TFormData>,
27
+ TName extends DeepKeys<TParentData>,
25
28
  /**
26
29
  * If TData is unknown, we can use the TName generic to determine the type
27
30
  */
28
- TData = unknown extends _TData ? DeepValue<TFormData, TName> : _TData,
31
+ TData = DeepValue<TParentData, TName>,
29
32
  > {
30
- name: TName
33
+ name: DeepKeys<TParentData>
31
34
  index?: TData extends any[] ? number : never
32
35
  defaultValue?: TData
33
36
  asyncDebounceMs?: number
34
37
  asyncAlways?: boolean
35
- onMount?: (formApi: FieldApi<TData, TFormData>) => void
36
- onChange?: ValidateFn<TData, TFormData>
37
- onChangeAsync?: ValidateAsyncFn<TData, TFormData>
38
+ onMount?: (formApi: FieldApi<TParentData, TName>) => void
39
+ onChange?: ValidateFn<TParentData, TName, TData>
40
+ onChangeAsync?: ValidateAsyncFn<TParentData, TName, TData>
38
41
  onChangeAsyncDebounceMs?: number
39
- onBlur?: ValidateFn<TData, TFormData>
40
- onBlurAsync?: ValidateAsyncFn<TData, TFormData>
42
+ onBlur?: ValidateFn<TParentData, TName, TData>
43
+ onBlurAsync?: ValidateAsyncFn<TParentData, TName, TData>
41
44
  onBlurAsyncDebounceMs?: number
42
- onSubmitAsync?: ValidateAsyncFn<TData, TFormData>
45
+ onSubmitAsync?: ValidateAsyncFn<TParentData, TName, TData>
43
46
  defaultMeta?: Partial<FieldMeta>
44
47
  }
45
48
 
46
49
  export interface FieldApiOptions<
47
- _TData,
48
- TFormData,
49
- /**
50
- * This allows us to restrict the name to only be a valid field name while
51
- * also assigning it to a generic
52
- */
53
- TName = unknown extends TFormData ? string : DeepKeys<TFormData>,
54
- /**
55
- * If TData is unknown, we can use the TName generic to determine the type
56
- */
57
- TData = unknown extends _TData ? DeepValue<TFormData, TName> : _TData,
58
- > extends FieldOptions<_TData, TFormData, TName, TData> {
59
- form: FormApi<TFormData>
50
+ TParentData,
51
+ TName extends DeepKeys<TParentData>,
52
+ TData = DeepValue<TParentData, TName>,
53
+ > extends FieldOptions<TParentData, TName, TData> {
54
+ form: FormApi<TParentData>
60
55
  }
61
56
 
62
57
  export type FieldMeta = {
@@ -74,43 +69,26 @@ export type FieldState<TData> = {
74
69
  meta: FieldMeta
75
70
  }
76
71
 
77
- type GetTData<
78
- TData,
79
- TFormData,
80
- Opts extends FieldApiOptions<TData, TFormData>,
81
- > = Opts extends FieldApiOptions<
82
- infer _TData,
83
- infer _TFormData,
84
- infer _TName,
85
- infer RealTData
86
- >
87
- ? RealTData
88
- : never
72
+ export type ResolveName<TParentData> = unknown extends TParentData
73
+ ? string
74
+ : DeepKeys<TParentData>
89
75
 
90
76
  export class FieldApi<
91
- _TData,
92
- TFormData,
93
- Opts extends FieldApiOptions<_TData, TFormData> = FieldApiOptions<
94
- _TData,
95
- TFormData
96
- >,
97
- TData extends GetTData<_TData, TFormData, Opts> = GetTData<
98
- _TData,
99
- TFormData,
100
- Opts
101
- >,
77
+ TParentData,
78
+ TName extends DeepKeys<TParentData>,
79
+ TData = DeepValue<TParentData, TName>,
102
80
  > {
103
81
  uid: number
104
- form: Opts['form']
105
- name!: DeepKeys<TFormData>
106
- options: Opts = {} as any
82
+ form: FieldApiOptions<TParentData, TName, TData>['form']
83
+ name!: DeepKeys<TParentData>
84
+ options: FieldApiOptions<TParentData, TName> = {} as any
107
85
  store!: Store<FieldState<TData>>
108
86
  state!: FieldState<TData>
109
87
  prevState!: FieldState<TData>
110
88
 
111
89
  constructor(
112
- opts: Opts & {
113
- form: FormApi<TFormData>
90
+ opts: FieldApiOptions<TParentData, TName, TData> & {
91
+ form: FormApi<TParentData>
114
92
  },
115
93
  ) {
116
94
  this.form = opts.form
@@ -190,12 +168,12 @@ export class FieldApi<
190
168
  }
191
169
  }
192
170
 
193
- update = (opts: FieldApiOptions<TData, TFormData>) => {
171
+ update = (opts: FieldApiOptions<TParentData, TName, TData>) => {
194
172
  // Default Value
195
173
  // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
196
174
  if (this.state.value === undefined) {
197
175
  const formDefault =
198
- opts.form.options.defaultValues?.[opts.name as keyof TFormData]
176
+ opts.form.options.defaultValues?.[opts.name as keyof TParentData]
199
177
 
200
178
  if (opts.defaultValue !== undefined) {
201
179
  this.setValue(opts.defaultValue as never)
@@ -213,7 +191,7 @@ export class FieldApi<
213
191
  }
214
192
 
215
193
  getValue = (): TData => {
216
- return this.form.getFieldValue(this.name)
194
+ return this.form.getFieldValue(this.name) as any
217
195
  }
218
196
 
219
197
  setValue = (
@@ -254,11 +232,16 @@ export class FieldApi<
254
232
  swapValues = (aIndex: number, bIndex: number) =>
255
233
  this.form.swapFieldValues(this.name, aIndex, bIndex)
256
234
 
257
- getSubField = <TName extends DeepKeys<TData>>(name: TName) =>
258
- new FieldApi<DeepValue<TData, TName>, TFormData>({
235
+ getSubField = <
236
+ TSubName extends DeepKeys<TData>,
237
+ TSubData = DeepValue<TData, TSubName>,
238
+ >(
239
+ name: TSubName,
240
+ ): FieldApi<TData, TSubName, TSubData> =>
241
+ new FieldApi({
259
242
  name: `${this.name}.${name}` as never,
260
243
  form: this.form,
261
- })
244
+ }) as any
262
245
 
263
246
  validateSync = (value = this.state.value, cause: ValidationCause) => {
264
247
  const { onChange, onBlur } = this.options
package/src/FormApi.ts CHANGED
@@ -31,7 +31,7 @@ export type FormOptions<TData> = {
31
31
  }
32
32
 
33
33
  export type FieldInfo<TFormData> = {
34
- instances: Record<string, FieldApi<any, TFormData>>
34
+ instances: Record<string, FieldApi<TFormData, any, any>>
35
35
  } & ValidationMeta
36
36
 
37
37
  export type ValidationMeta = {
@@ -106,7 +106,7 @@ export class FormApi<TFormData> {
106
106
  constructor(opts?: FormOptions<TFormData>) {
107
107
  this.store = new Store<FormState<TFormData>>(
108
108
  getDefaultFormState({
109
- ...opts?.defaultState,
109
+ ...(opts?.defaultState as any),
110
110
  values: opts?.defaultValues ?? opts?.defaultState?.values,
111
111
  isFormValid: true,
112
112
  }),
@@ -174,7 +174,7 @@ export class FormApi<TFormData> {
174
174
  getDefaultFormState(
175
175
  Object.assign(
176
176
  {},
177
- this.state,
177
+ this.state as any,
178
178
  // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
179
179
  shouldUpdateState ? options.defaultState : {},
180
180
  // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
@@ -194,7 +194,7 @@ export class FormApi<TFormData> {
194
194
  reset = () =>
195
195
  this.store.setState(() =>
196
196
  getDefaultFormState({
197
- ...this.options.defaultState,
197
+ ...(this.options.defaultState as any),
198
198
  values: this.options.defaultValues ?? this.options.defaultState?.values,
199
199
  }),
200
200
  )
@@ -288,7 +288,9 @@ export class FormApi<TFormData> {
288
288
  return this.state.fieldMeta[field]
289
289
  }
290
290
 
291
- getFieldInfo = <TField extends DeepKeys<TFormData>>(field: TField) => {
291
+ getFieldInfo = <TField extends DeepKeys<TFormData>>(
292
+ field: TField,
293
+ ): FieldInfo<TFormData> => {
292
294
  // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
293
295
  return (this.fieldInfo[field] ||= {
294
296
  instances: {},
package/src/utils.ts CHANGED
@@ -133,22 +133,28 @@ type AllowedIndexes<
133
133
  ? AllowedIndexes<Tail, Keys | Tail['length']>
134
134
  : Keys
135
135
 
136
- export type DeepKeys<T> = unknown extends T
137
- ? keyof T
136
+ export type DeepKeys<T, TDepth extends any[] = []> = TDepth['length'] extends 5
137
+ ? never
138
+ : unknown extends T
139
+ ? string
138
140
  : object extends T
139
141
  ? string
140
142
  : T extends readonly any[] & IsTuple<T>
141
- ? AllowedIndexes<T> | DeepKeysPrefix<T, AllowedIndexes<T>>
143
+ ? AllowedIndexes<T> | DeepKeysPrefix<T, AllowedIndexes<T>, TDepth>
142
144
  : T extends any[]
143
- ? DeepKeys<T[number]>
145
+ ? DeepKeys<T[number], [...TDepth, any]>
144
146
  : T extends Date
145
147
  ? never
146
148
  : T extends object
147
- ? (keyof T & string) | DeepKeysPrefix<T, keyof T>
149
+ ? (keyof T & string) | DeepKeysPrefix<T, keyof T, TDepth>
148
150
  : never
149
151
 
150
- type DeepKeysPrefix<T, TPrefix> = TPrefix extends keyof T & (number | string)
151
- ? `${TPrefix}.${DeepKeys<T[TPrefix]> & string}`
152
+ type DeepKeysPrefix<
153
+ T,
154
+ TPrefix,
155
+ TDepth extends any[],
156
+ > = TPrefix extends keyof T & (number | string)
157
+ ? `${TPrefix}.${DeepKeys<T[TPrefix], [...TDepth, any]> & string}`
152
158
  : never
153
159
 
154
160
  export type DeepValue<T, TProp> = T extends Record<string | number, any>