Skip to content

feat: refactor useWatch type #608

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all 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
14 changes: 11 additions & 3 deletions src/namePathType.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,26 @@ export type DeepNamePath<
? ParentNamePath['length'] extends 0
? Store | BaseNamePath // Return `BaseNamePath` instead of array if `ParentNamePath` is empty
: Store extends any[]
? [...ParentNamePath, number] // Connect path
? readonly [...ParentNamePath, number] // Connect path
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这里为什么要加 readonly?

Copy link
Contributor Author

@crazyair crazyair Aug 2, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

: never
: Store extends any[] // Check if `Store` is `any[]`
? // Connect path. e.g. { a: { b: string }[] }
// Get: [a] | [ a,number] | [ a ,number , b]
[...ParentNamePath, number] | DeepNamePath<Store[number], [...ParentNamePath, number]>
readonly [...ParentNamePath, number] | DeepNamePath<Store[number], [...ParentNamePath, number]>
: {
// Convert `Store` to <key, value>. We mark key a `FieldKey`
[FieldKey in keyof Store]: Store[FieldKey] extends Function
? never
:
| (ParentNamePath['length'] extends 0 ? FieldKey : never) // If `ParentNamePath` is empty, it can use `FieldKey` without array path
| [...ParentNamePath, FieldKey] // Exist `ParentNamePath`, connect it
| readonly [...ParentNamePath, FieldKey] // Exist `ParentNamePath`, connect it
| DeepNamePath<Required<Store>[FieldKey], [...ParentNamePath, FieldKey]>; // If `Store[FieldKey]` is object
}[keyof Store];

export type GetNameType<
Store = any,
NamePath extends readonly any[] = [],
NamePathCache extends readonly any[] = [],
> = NamePathCache['length'] extends NamePath['length']
? Store
: GetNameType<Store[NamePath[NamePathCache['length']]], NamePath, [...NamePathCache, true]>;
39 changes: 8 additions & 31 deletions src/useWatch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import type {
import { useState, useContext, useEffect, useRef, useMemo } from 'react';
import { getNamePath, getValue } from './utils/valueUtil';
import { isFormInstance } from './utils/typeUtil';
import type { DeepNamePath, GetNameType } from './namePathType';

type ReturnPromise<T> = T extends Promise<infer ValueType> ? ValueType : never;
type GetGeneric<TForm extends FormInstance> = ReturnPromise<ReturnType<TForm['validateFields']>>;
Expand All @@ -37,39 +38,15 @@ const useWatchWarning =
: () => {};

function useWatch<
TDependencies1 extends keyof GetGeneric<TForm>,
TForm extends FormInstance,
TDependencies2 extends keyof GetGeneric<TForm>[TDependencies1],
TDependencies3 extends keyof GetGeneric<TForm>[TDependencies1][TDependencies2],
TDependencies4 extends keyof GetGeneric<TForm>[TDependencies1][TDependencies2][TDependencies3],
TForm extends FormInstance = FormInstance,
const TDependencies extends DeepNamePath<GetGeneric<TForm>> = any,
>(
dependencies: [TDependencies1, TDependencies2, TDependencies3, TDependencies4],
dependencies: TDependencies,
form?: TForm | WatchOptions<TForm>,
): GetGeneric<TForm>[TDependencies1][TDependencies2][TDependencies3][TDependencies4];

function useWatch<
TDependencies1 extends keyof GetGeneric<TForm>,
TForm extends FormInstance,
TDependencies2 extends keyof GetGeneric<TForm>[TDependencies1],
TDependencies3 extends keyof GetGeneric<TForm>[TDependencies1][TDependencies2],
>(
dependencies: [TDependencies1, TDependencies2, TDependencies3],
form?: TForm | WatchOptions<TForm>,
): GetGeneric<TForm>[TDependencies1][TDependencies2][TDependencies3];

function useWatch<
TDependencies1 extends keyof GetGeneric<TForm>,
TForm extends FormInstance,
TDependencies2 extends keyof GetGeneric<TForm>[TDependencies1],
>(
dependencies: [TDependencies1, TDependencies2],
form?: TForm | WatchOptions<TForm>,
): GetGeneric<TForm>[TDependencies1][TDependencies2];

function useWatch<TDependencies extends keyof GetGeneric<TForm>, TForm extends FormInstance>(
dependencies: TDependencies | [TDependencies],
form?: TForm | WatchOptions<TForm>,
): GetGeneric<TForm>[TDependencies];
): GetNameType<
GetGeneric<TForm>,
TDependencies extends readonly any[] ? TDependencies : [TDependencies]
>;

function useWatch<TForm extends FormInstance>(
dependencies: [],
Expand Down
24 changes: 21 additions & 3 deletions tests/useWatch.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,7 @@ describe('useWatch', () => {
demo?: string;
demo2?: string;
id?: number;
demo1?: { demo2?: { demo3?: { demo4?: string } } };
demo1?: { demo2?: { demo3?: { demo4?: { demo5?: { demo6: { demo7?: string } } } } } };
};

const Demo: React.FC = () => {
Expand All @@ -271,10 +271,28 @@ describe('useWatch', () => {
const demo3 = Form.useWatch(['demo1', 'demo2', 'demo3'], form);
const demo4 = Form.useWatch(['demo1', 'demo2', 'demo3', 'demo4'], form);
const demo5 = Form.useWatch(['demo1', 'demo2', 'demo3', 'demo4', 'demo5'], form);
const more = Form.useWatch(['age', 'name', 'gender'], form);
const demo6 = Form.useWatch(['demo1', 'demo2', 'demo3', 'demo4', 'demo5', 'demo6'], form);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这里测试不会有效果,要加个类型推断做匹配:

const demoX: XXX = Form.useWatch(...)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

要加ts报错的测试吗

const demo7 = Form.useWatch(
['demo1', 'demo2', 'demo3', 'demo4', 'demo5', 'demo6', 'demo7'],
form,
);
const demo = Form.useWatch<string>(['demo']);
return (
<>{JSON.stringify({ values, main, age, demo1, demo2, demo3, demo4, demo5, more, demo })}</>
<>
{JSON.stringify({
values,
main,
age,
demo1,
demo2,
demo3,
demo4,
demo5,
demo6,
demo7,
demo,
})}
</>
);
};

Expand Down