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"
},
"resolutions": {
"@types/react": "^17.0.0",
"@types/react-dom": "^17.0.0",
"@types/react": "18.3.18",
"@types/react-dom": "^18.0.0",
"@typescript-eslint/parser": "^6.2.0",
"react-router-dom": "^6.11.2",
"react-router": "^6.11.2",
"react": "^18.0.0",
"react-dom": "^18.0.0",
"nwsapi": "2.2.7",
"antd": "5.12.8"
"antd": "5.12.8",
"@ant-design/icons": "^5.6.1"
},
"config": {
"ghooks": {
@ -72,13 +73,14 @@
"@commitlint/cli": "^16.1.0",
"@commitlint/config-conventional": "^16.0.0",
"@commitlint/prompt-cli": "^16.1.0",
"@types/react": "^17.0.0",
"@types/react-dom": "^17.0.0",
"@types/react": "18.3.18",
"@types/react-dom": "^18.0.0",
"auto-changelog": "^2.4.0",
"eslint-plugin-jest-dom": "^5.0.1",
"eslint-plugin-testing-library": "^5.11.0",
"ghooks": "^2.0.4",
"lint-staged": "^13.2.3",
"patch-package": "^8.0.0",
"pretty-format": "^24.0.0",
"pretty-quick": "^3.1.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 { 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() {
const excludePath = resolve(process.cwd(), '.git', 'info', 'exclude');
const content = 'packages/pro-plugins/\n';
@ -47,6 +55,7 @@ module.exports = (cli) => {
.allowUnknownOption()
.option('--skip-umi')
.action(async (options) => {
runPatchPackage();
writeToExclude();
generatePlugins();
generatePlaywrightPath(true);

View File

@ -8,7 +8,7 @@
"dependencies": {
"@ahooksjs/use-url-state": "3.5.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",
"@antv/g2plot": "^2.4.18",
"@budibase/handlebars-helpers": "^0.14.0",

View File

@ -235,7 +235,7 @@ export class Application {
this.addComponents({
Link,
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) */
configurableProperties?: Record<string, ISchema>;
/** Available field types for the current template */
availableFieldInterfaces?: AvailableFieldInterfacesInclude | AvailableFieldInterfacesExclude;
availableFieldInterfaces?: AvailableFieldInterfacesInclude & AvailableFieldInterfacesExclude;
/** Whether it is a divider */
divider?: boolean;
/** Template description */

View File

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

View File

@ -7,13 +7,17 @@
* 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 { get } from 'lodash';
import { useImported, loadableResource } from 'react-imported-component';
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.
*
@ -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.
* @returns {Record<K, React.LazyExoticComponent<M[K]>>} An object containing the lazy-loaded components.
*/
export function lazy<M extends ComponentType<any>>(
factory: () => Promise<{ default: M }>,
): React.LazyExoticComponent<M>;
export function lazy<M extends Record<'default', any>>(factory: () => Promise<M>): M['default'];
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>,
...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>,
...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 />}>
<LazyComponent {...props} />
</React.Suspense>
);
)) as M[K];
return acc;
},
{} as Record<K, React.ComponentType<any>>,
{} as LazyComponentType<M, K>,
);
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -18,7 +18,7 @@ describe('IconPicker', () => {
const button = container.querySelector('button') as HTMLButtonElement;
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 () => {
@ -50,9 +50,9 @@ describe('IconPicker', () => {
const searchInput = screen.queryByRole('search') as HTMLInputElement;
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 waitFor(() => expect(screen.queryAllByRole('img').length).toBeLessThan(422));
await waitFor(() => expect(screen.queryAllByRole('img').length).toBeLessThan(448));
await userEvent.clear(searchInput);
await userEvent.type(searchInput, 'abcd');
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,
sideMenuSchema,

View File

@ -8,7 +8,6 @@
*/
import _ from 'lodash';
// @ts-ignore
import React, { FC, startTransition, useEffect, useState } from 'react';
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(
() => ({
right: render(),
left: contextProps?.tabBarExtraContent,
left: contextProps?.tabBarExtraContent as React.ReactNode,
}),
[contextProps?.tabBarExtraContent, render],
);

View File

@ -447,6 +447,8 @@ export function TextArea(props) {
onPaste={onPaste}
onCompositionStart={onCompositionStart}
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}
style={style}
className={cx(

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -297,7 +297,15 @@ const PopoverContent = React.forwardRef((props: any, ref) => {
});
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 {
store: {
data: { item, ports, data },

View File

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

View File

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

View File

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

View File

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