chore: upgrade react types definition (#6278)

* chore: upgrade react type to 18

* chore: update ant design icons to avoid ts error

* fix: ts errors

* fix: ts error after upgrade react types

* fix: some icons ts error

* fix: improve type validation in bulk edit form item settings

* fix: lazy load component type error

* fix: some ts errors

* fix: unit test error after upgrade ant design icons

* chore: remove ts-ignore comment for startTransition
This commit is contained in:
gchust 2025-02-25 09:48:13 +08:00 committed by GitHub
parent 669c7f6335
commit 3cbf81e5cd
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
29 changed files with 95 additions and 50 deletions

View File

@ -44,15 +44,16 @@
"run:example": "tsx -r dotenv/config -r tsconfig-paths/register ./examples/index.ts" "run:example": "tsx -r dotenv/config -r tsconfig-paths/register ./examples/index.ts"
}, },
"resolutions": { "resolutions": {
"@types/react": "^17.0.0", "@types/react": "18.3.18",
"@types/react-dom": "^17.0.0", "@types/react-dom": "^18.0.0",
"@typescript-eslint/parser": "^6.2.0", "@typescript-eslint/parser": "^6.2.0",
"react-router-dom": "^6.11.2", "react-router-dom": "^6.11.2",
"react-router": "^6.11.2", "react-router": "^6.11.2",
"react": "^18.0.0", "react": "^18.0.0",
"react-dom": "^18.0.0", "react-dom": "^18.0.0",
"nwsapi": "2.2.7", "nwsapi": "2.2.7",
"antd": "5.12.8" "antd": "5.12.8",
"@ant-design/icons": "^5.6.1"
}, },
"config": { "config": {
"ghooks": { "ghooks": {
@ -72,13 +73,14 @@
"@commitlint/cli": "^16.1.0", "@commitlint/cli": "^16.1.0",
"@commitlint/config-conventional": "^16.0.0", "@commitlint/config-conventional": "^16.0.0",
"@commitlint/prompt-cli": "^16.1.0", "@commitlint/prompt-cli": "^16.1.0",
"@types/react": "^17.0.0", "@types/react": "18.3.18",
"@types/react-dom": "^17.0.0", "@types/react-dom": "^18.0.0",
"auto-changelog": "^2.4.0", "auto-changelog": "^2.4.0",
"eslint-plugin-jest-dom": "^5.0.1", "eslint-plugin-jest-dom": "^5.0.1",
"eslint-plugin-testing-library": "^5.11.0", "eslint-plugin-testing-library": "^5.11.0",
"ghooks": "^2.0.4", "ghooks": "^2.0.4",
"lint-staged": "^13.2.3", "lint-staged": "^13.2.3",
"patch-package": "^8.0.0",
"pretty-format": "^24.0.0", "pretty-format": "^24.0.0",
"pretty-quick": "^3.1.0", "pretty-quick": "^3.1.0",
"react": "^18.0.0", "react": "^18.0.0",

View File

@ -14,6 +14,14 @@ const { existsSync, mkdirSync, readFileSync, appendFileSync } = require('fs');
const { readFile, writeFile } = require('fs').promises; const { readFile, writeFile } = require('fs').promises;
const { createStoragePluginsSymlink, createDevPluginsSymlink } = require('@nocobase/utils/plugin-symlink'); const { createStoragePluginsSymlink, createDevPluginsSymlink } = require('@nocobase/utils/plugin-symlink');
function runPatchPackage() {
// run yarn patch-package
// console.log('patching third party packages...');
run('yarn', ['patch-package'], {
stdio: 'pipe',
});
}
function writeToExclude() { function writeToExclude() {
const excludePath = resolve(process.cwd(), '.git', 'info', 'exclude'); const excludePath = resolve(process.cwd(), '.git', 'info', 'exclude');
const content = 'packages/pro-plugins/\n'; const content = 'packages/pro-plugins/\n';
@ -47,6 +55,7 @@ module.exports = (cli) => {
.allowUnknownOption() .allowUnknownOption()
.option('--skip-umi') .option('--skip-umi')
.action(async (options) => { .action(async (options) => {
runPatchPackage();
writeToExclude(); writeToExclude();
generatePlugins(); generatePlugins();
generatePlaywrightPath(true); generatePlaywrightPath(true);

View File

@ -8,7 +8,7 @@
"dependencies": { "dependencies": {
"@ahooksjs/use-url-state": "3.5.1", "@ahooksjs/use-url-state": "3.5.1",
"@ant-design/cssinjs": "^1.11.1", "@ant-design/cssinjs": "^1.11.1",
"@ant-design/icons": "^5.1.4", "@ant-design/icons": "^5.6.1",
"@ant-design/pro-layout": "^7.16.11", "@ant-design/pro-layout": "^7.16.11",
"@antv/g2plot": "^2.4.18", "@antv/g2plot": "^2.4.18",
"@budibase/handlebars-helpers": "^0.14.0", "@budibase/handlebars-helpers": "^0.14.0",

View File

@ -235,7 +235,7 @@ export class Application {
this.addComponents({ this.addComponents({
Link, Link,
Navigate: Navigate as ComponentType, Navigate: Navigate as ComponentType,
NavLink, NavLink: NavLink as ComponentType,
}); });
} }

View File

@ -63,7 +63,7 @@ export abstract class CollectionTemplate {
/** UI configurable CollectionOptions parameters (fields for adding or editing Collection forms) */ /** UI configurable CollectionOptions parameters (fields for adding or editing Collection forms) */
configurableProperties?: Record<string, ISchema>; configurableProperties?: Record<string, ISchema>;
/** Available field types for the current template */ /** Available field types for the current template */
availableFieldInterfaces?: AvailableFieldInterfacesInclude | AvailableFieldInterfacesExclude; availableFieldInterfaces?: AvailableFieldInterfacesInclude & AvailableFieldInterfacesExclude;
/** Whether it is a divider */ /** Whether it is a divider */
divider?: boolean; divider?: boolean;
/** Template description */ /** Template description */

View File

@ -44,7 +44,7 @@ export const useMenuItem = () => {
const renderItems = useRef<() => JSX.Element>(null); const renderItems = useRef<() => JSX.Element>(null);
const shouldRerender = useRef(false); const shouldRerender = useRef(false);
const Component = useCallback(({ limitCount }) => { const Component = useCallback(({ limitCount }: { limitCount?: number }) => {
if (!shouldRerender.current) { if (!shouldRerender.current) {
return null; return null;
} }

View File

@ -7,13 +7,17 @@
* For more information, please refer to: https://www.nocobase.com/agreement. * For more information, please refer to: https://www.nocobase.com/agreement.
*/ */
import React, { ComponentType, lazy as ReactLazy } from 'react'; import React, { lazy as ReactLazy } from 'react';
import { Spin } from 'antd'; import { Spin } from 'antd';
import { get } from 'lodash'; import { get } from 'lodash';
import { useImported, loadableResource } from 'react-imported-component'; import { useImported, loadableResource } from 'react-imported-component';
export const LAZY_COMPONENT_KEY = Symbol('LAZY_COMPONENT_KEY'); export const LAZY_COMPONENT_KEY = Symbol('LAZY_COMPONENT_KEY');
type LazyComponentType<M extends Record<string, any>, K extends keyof M> = {
[P in K]: M[P];
};
/** /**
* Lazily loads a React component or multiple components. * Lazily loads a React component or multiple components.
* *
@ -31,16 +35,14 @@ export const LAZY_COMPONENT_KEY = Symbol('LAZY_COMPONENT_KEY');
* @param {...K[]} componentNames - The names of the components to be lazy-loaded from the module. * @param {...K[]} componentNames - The names of the components to be lazy-loaded from the module.
* @returns {Record<K, React.LazyExoticComponent<M[K]>>} An object containing the lazy-loaded components. * @returns {Record<K, React.LazyExoticComponent<M[K]>>} An object containing the lazy-loaded components.
*/ */
export function lazy<M extends ComponentType<any>>( export function lazy<M extends Record<'default', any>>(factory: () => Promise<M>): M['default'];
factory: () => Promise<{ default: M }>,
): React.LazyExoticComponent<M>;
export function lazy<M extends Record<string, any>, K extends keyof M & string>( export function lazy<M extends Record<string, any>, K extends keyof M = keyof M>(
factory: () => Promise<M>, factory: () => Promise<M>,
...componentNames: K[] ...componentNames: K[]
): Record<K, React.LazyExoticComponent<M[K]>>; ): LazyComponentType<M, K>;
export function lazy<M extends Record<string, any>, K extends keyof M & string>( export function lazy<M extends Record<string, any>, K extends keyof M>(
factory: () => Promise<M>, factory: () => Promise<M>,
...componentNames: K[] ...componentNames: K[]
) { ) {
@ -73,14 +75,14 @@ export function lazy<M extends Record<string, any>, K extends keyof M & string>(
}; };
}), }),
); );
acc[name] = (props) => ( acc[name] = ((props) => (
<React.Suspense fallback={<Spin />}> <React.Suspense fallback={<Spin />}>
<LazyComponent {...props} /> <LazyComponent {...props} />
</React.Suspense> </React.Suspense>
); )) as M[K];
return acc; return acc;
}, },
{} as Record<K, React.ComponentType<any>>, {} as LazyComponentType<M, K>,
); );
} }

View File

@ -14,7 +14,6 @@ import React, {
createContext, createContext,
FC, FC,
memo, memo,
// @ts-ignore
startTransition, startTransition,
useCallback, useCallback,
useContext, useContext,

View File

@ -28,12 +28,14 @@ export const createPortalProvider = (id: string | symbol) => {
<Fragment> <Fragment>
{props.children} {props.children}
<Observer> <Observer>
{() => { {
if (!props.id) return <></>; (() => {
const portal = PortalMap.get(props.id); if (!props.id) return <></>;
if (portal) return createPortal(portal, document.body); const portal = PortalMap.get(props.id);
return <></>; if (portal) return createPortal(portal, document.body);
}} return <></>;
}) as unknown as React.ReactNode
}
</Observer> </Observer>
</Fragment> </Fragment>
); );

View File

@ -10,7 +10,6 @@
import { observer, RecursionField, useField, useFieldSchema } from '@formily/react'; import { observer, RecursionField, useField, useFieldSchema } from '@formily/react';
import { Drawer } from 'antd'; import { Drawer } from 'antd';
import classNames from 'classnames'; import classNames from 'classnames';
// @ts-ignore
import React, { FC, startTransition, useCallback, useEffect, useMemo, useState } from 'react'; import React, { FC, startTransition, useCallback, useEffect, useMemo, useState } from 'react';
import { ErrorBoundary, FallbackProps } from 'react-error-boundary'; import { ErrorBoundary, FallbackProps } from 'react-error-boundary';
import { NocoBaseRecursionField } from '../../../formily/NocoBaseRecursionField'; import { NocoBaseRecursionField } from '../../../formily/NocoBaseRecursionField';

View File

@ -12,7 +12,6 @@ import { observer, useField, useFieldSchema } from '@formily/react';
import { Modal, ModalProps } from 'antd'; import { Modal, ModalProps } from 'antd';
import classNames from 'classnames'; import classNames from 'classnames';
import { ErrorBoundary, FallbackProps } from 'react-error-boundary'; import { ErrorBoundary, FallbackProps } from 'react-error-boundary';
// @ts-ignore
import React, { FC, startTransition, useEffect, useMemo, useState } from 'react'; import React, { FC, startTransition, useEffect, useMemo, useState } from 'react';
import { NocoBaseRecursionField } from '../../../formily/NocoBaseRecursionField'; import { NocoBaseRecursionField } from '../../../formily/NocoBaseRecursionField';
import { useToken } from '../../../style'; import { useToken } from '../../../style';

View File

@ -8,7 +8,6 @@
*/ */
import { observer, useFieldSchema } from '@formily/react'; import { observer, useFieldSchema } from '@formily/react';
// @ts-ignore
import React, { FC, startTransition, useEffect, useMemo, useState } from 'react'; import React, { FC, startTransition, useEffect, useMemo, useState } from 'react';
import { createPortal } from 'react-dom'; import { createPortal } from 'react-dom';
import { ActionContextNoRerender, useActionContext } from '.'; import { ActionContextNoRerender, useActionContext } from '.';

View File

@ -48,7 +48,7 @@ export const AssociationFilterItem = withDynamicSchemaProps(
const [searchVisible, setSearchVisible] = useState(false); const [searchVisible, setSearchVisible] = useState(false);
const defaultActiveKeyCollapse = useMemo<React.Key[]>( const defaultActiveKeyCollapse = useMemo<string[]>(
() => (defaultCollapse && collectionField?.name ? [collectionField.name] : []), () => (defaultCollapse && collectionField?.name ? [collectionField.name] : []),
[collectionField?.name, defaultCollapse], [collectionField?.name, defaultCollapse],
); );

View File

@ -18,7 +18,7 @@ describe('IconPicker', () => {
const button = container.querySelector('button') as HTMLButtonElement; const button = container.querySelector('button') as HTMLButtonElement;
await userEvent.click(button); await userEvent.click(button);
expect(screen.queryAllByRole('img').length).toBe(422); expect(screen.queryAllByRole('img').length).toBe(448);
}); });
it('should display the selected icon', async () => { it('should display the selected icon', async () => {
@ -50,9 +50,9 @@ describe('IconPicker', () => {
const searchInput = screen.queryByRole('search') as HTMLInputElement; const searchInput = screen.queryByRole('search') as HTMLInputElement;
await waitFor(() => expect(searchInput).toBeInTheDocument()); await waitFor(() => expect(searchInput).toBeInTheDocument());
expect(screen.queryAllByRole('img').length).toBe(422); expect(screen.queryAllByRole('img').length).toBe(448);
await userEvent.type(searchInput, 'left'); await userEvent.type(searchInput, 'left');
await waitFor(() => expect(screen.queryAllByRole('img').length).toBeLessThan(422)); await waitFor(() => expect(screen.queryAllByRole('img').length).toBeLessThan(448));
await userEvent.clear(searchInput); await userEvent.clear(searchInput);
await userEvent.type(searchInput, 'abcd'); await userEvent.type(searchInput, 'abcd');
await waitFor(() => { await waitFor(() => {

View File

@ -398,7 +398,12 @@ const HeaderMenu = React.memo<{
}, },
); );
const SideMenu = React.memo<any>( type SideMenuProps = Omit<MenuProps, 'mode'> & {
mode: 'mix' | MenuProps['mode'];
[key: string]: any;
};
const SideMenu = React.memo<SideMenuProps>(
({ ({
mode, mode,
sideMenuSchema, sideMenuSchema,

View File

@ -8,7 +8,6 @@
*/ */
import _ from 'lodash'; import _ from 'lodash';
// @ts-ignore
import React, { FC, startTransition, useEffect, useState } from 'react'; import React, { FC, startTransition, useEffect, useState } from 'react';
import { useKeepAlive } from '../../../route-switch/antd/admin-layout/KeepAlive'; import { useKeepAlive } from '../../../route-switch/antd/admin-layout/KeepAlive';

View File

@ -52,7 +52,7 @@ export const Tabs: any = React.memo((props: TabsProps) => {
const tabBarExtraContent = useMemo( const tabBarExtraContent = useMemo(
() => ({ () => ({
right: render(), right: render(),
left: contextProps?.tabBarExtraContent, left: contextProps?.tabBarExtraContent as React.ReactNode,
}), }),
[contextProps?.tabBarExtraContent, render], [contextProps?.tabBarExtraContent, render],
); );

View File

@ -447,6 +447,8 @@ export function TextArea(props) {
onPaste={onPaste} onPaste={onPaste}
onCompositionStart={onCompositionStart} onCompositionStart={onCompositionStart}
onCompositionEnd={onCompositionEnd} onCompositionEnd={onCompositionEnd}
// should use data-placeholder here, but not sure if it is safe to make the change, so add ignore here
// @ts-ignore
placeholder={props.placeholder} placeholder={props.placeholder}
style={style} style={style}
className={cx( className={cx(

View File

@ -395,7 +395,7 @@ export const DataBlockInitializer: FC<DataBlockInitializerProps> = (props) => {
}, },
children, children,
}, },
]; ] as MenuProps['items'];
}, [searchedChildren, hideChildrenIfSingleCollection, name, compile, title, icon, onClick, props]); }, [searchedChildren, hideChildrenIfSingleCollection, name, compile, title, icon, onClick, props]);
if (childItems.length > 1 || (childItems.length === 1 && childItems[0].children?.length > 0)) { if (childItems.length > 1 || (childItems.length === 1 && childItems[0].children?.length > 0)) {

View File

@ -15,7 +15,6 @@ import classNames from 'classnames';
import React, { import React, {
createContext, createContext,
FC, FC,
//@ts-ignore
startTransition, startTransition,
useCallback, useCallback,
useEffect, useEffect,

View File

@ -35,7 +35,6 @@ import React, {
FC, FC,
ReactNode, ReactNode,
createContext, createContext,
// @ts-ignore
startTransition, startTransition,
useCallback, useCallback,
useContext, useContext,

View File

@ -201,7 +201,7 @@ export const bulkEditFormItemSettings = new SchemaSettings({
const { form } = useFormBlockContext(); const { form } = useFormBlockContext();
const isFormReadPretty = useIsFormReadPretty(); const isFormReadPretty = useIsFormReadPretty();
const validateSchema = useValidateSchema(); const validateSchema = useValidateSchema();
return form && !isFormReadPretty && validateSchema; return form && !isFormReadPretty && Boolean(validateSchema);
}, },
}, },
fieldComponentSettingsItem, fieldComponentSettingsItem,

View File

@ -191,7 +191,7 @@ const EditCollectionAction = (props) => {
const schema = getSchema( const schema = getSchema(
{ {
...templateConf, ...templateConf,
}, } as unknown as IField,
record, record,
compile, compile,
getContainer, getContainer,

View File

@ -297,7 +297,15 @@ const PopoverContent = React.forwardRef((props: any, ref) => {
}); });
PopoverContent.displayName = 'PopoverContent'; PopoverContent.displayName = 'PopoverContent';
const PortsCom = React.memo<any>(({ targetGraph, collectionData, setTargetNode, node, loadCollections }) => { type PortsComProps = {
targetGraph: any;
collectionData: any;
setTargetNode: any;
node: any;
loadCollections: any;
};
const PortsCom = React.memo<PortsComProps>(({ targetGraph, collectionData, setTargetNode, node, loadCollections }) => {
const { const {
store: { store: {
data: { item, ports, data }, data: { item, ports, data },

View File

@ -19,8 +19,8 @@
"@formily/shared": "2.x", "@formily/shared": "2.x",
"@googlemaps/js-api-loader": "^1.16.1", "@googlemaps/js-api-loader": "^1.16.1",
"@types/google.maps": "^3.53.4", "@types/google.maps": "^3.53.4",
"@types/react": "^17.0.0", "@types/react": "^18.0.0",
"@types/react-dom": "^17.0.0", "@types/react-dom": "^18.0.0",
"ahooks": "^3.7.2", "ahooks": "^3.7.2",
"antd": "5.x", "antd": "5.x",
"react": "18.x", "react": "18.x",

View File

@ -14,8 +14,8 @@
"@formily/antd-v5": "1.x", "@formily/antd-v5": "1.x",
"@formily/react": "2.x", "@formily/react": "2.x",
"@formily/shared": "2.x", "@formily/shared": "2.x",
"@types/react": "17.x", "@types/react": "18.x",
"@types/react-dom": "17.x", "@types/react-dom": "18.x",
"ahooks": "3.x", "ahooks": "3.x",
"antd": "5.x", "antd": "5.x",
"antd-mobile": "^5.38", "antd-mobile": "^5.38",

View File

@ -23,8 +23,8 @@
"@formily/core": "2.x", "@formily/core": "2.x",
"@formily/react": "2.x", "@formily/react": "2.x",
"@formily/shared": "2.x", "@formily/shared": "2.x",
"@types/react": "17.x", "@types/react": "18.x",
"@types/react-dom": "17.x", "@types/react-dom": "18.x",
"ahooks": "3.x", "ahooks": "3.x",
"antd": "5.x", "antd": "5.x",
"antd-mobile": "^5.38", "antd-mobile": "^5.38",

View File

@ -566,8 +566,8 @@ export function SchemaConfigButton(props) {
const msg = validateForms(values.forms); const msg = validateForms(values.forms);
if (msg) { if (msg) {
message.error({ message.error({
// eslint-disable-next-line react-hooks/rules-of-hooks // message.error does not support title, and it will cause error, so comment it
title: t('Validation failed'), // title: t('Validation failed'),
content: t(msg), content: t(msg),
}); });
return; return;

View File

@ -0,0 +1,22 @@
diff --git a/node_modules/@types/react/index.d.ts b/node_modules/@types/react/index.d.ts
index 6ea73ef..c835599 100644
--- a/node_modules/@types/react/index.d.ts
+++ b/node_modules/@types/react/index.d.ts
@@ -1030,7 +1030,7 @@ declare namespace React {
forceUpdate(callback?: () => void): void;
render(): ReactNode;
- readonly props: Readonly<P>;
+ readonly props: Readonly<P & { children?: P extends { children: infer C } ? C : any }>;
state: Readonly<S>;
/**
* @deprecated
@@ -1125,7 +1125,7 @@ declare namespace React {
*/
interface FunctionComponent<P = {}> {
(
- props: P,
+ props: P & { children?: P extends { children: infer C } ? C : any },
/**
* @deprecated
*