TypeScript, React.ReactNode, withRouter и подключиться - PullRequest
0 голосов
/ 05 января 2019

Обновление 2 Основываясь на втором комментарии artem , я смог решить мою проблему, изменив

component: <TestRoute />

до

component: TestRoute

Обновление Основываясь на комментарии artem , я изменил свой код следующим образом, и ошибка, с которой я столкнулся, исчезла, но заменена новой, на этот раз в конструкторе, которому я назначаю свойство компонента:

ОШИБКА в [at-loader] ./src/ts/components/app/StandardRouter.tsx:34:13 TS2322: тип «Элемент» нельзя назначить типу «ComponentClass | FunctionComponent. Тип «Элемент» не может быть назначен типу «FunctionComponent». Тип «Элемент» не обеспечивает совпадения для подписи »(реквизиты: любой, контекст ?: любой): ReactElement | нуль».

StandardRouter.tsx

// React router component. Connects all routes to the application.
import * as React from 'react';
import { connect } from 'react-redux';
import { Route, Switch, withRouter } from 'react-router-dom';
import { ConnectedRouter } from 'connected-react-router';
import { IntlProvider } from 'react-intl';
import _forEach from 'lodash/forEach';
import _find from 'lodash/find';
import { History, createHashHistory } from 'history';
import { connectRouter } from 'connected-react-router';
import reducerRegistry from '../../api/private/core/reducer-registry';
import routeRegistry from '../../api/private/core/route-registry';
import RouteObject from '../../interfaces/route-object';
import TestRoute from '../test/test-route';

class StandardRouter extends React.Component {
    constructor(props: object) {
        super(props);
        this._history = createHashHistory();
        reducerRegistry.register({
            name: 'router',
            key: 'cb3dceb0-367b-4c2a-a0fb-a8d8671283e2',
            reducer: connectRouter(this._history)
        });
        routeRegistry.setChangeListener(() => {
            if (this._isMounted) {
                this.forceUpdate();
            } else if (!this._isMounted && !this._doUpdate) {
                this._doUpdate = true;
            }
        });
        routeRegistry.register({
            key: '90e8246f-96a6-49a5-920a-a2cf0d347fcd',
            component: <TestRoute />, // <-- New error here
            paths: '/',
            text: 'Test 123'
        });
    }

    componentDidMount(): void {
        this._isMounted = true;
        if (this._doUpdate) {
            this.forceUpdate();
        }
    }

    private _history: History;
    private _isMounted: boolean = false;
    private _doUpdate: boolean = false;

    chooseRender(path: string, Comp: React.ComponentType): React.ReactNode {
        return <Comp />;
    }

    // Creates Route objects based on routes collection from registry
    renderPages(): React.ReactNode[] {
        const routes: React.ReactNode[] = [];
        const loadedRoutes: RouteObject[] = routeRegistry.getRoutes();
        _forEach(loadedRoutes, page => {
            const Component = withRouter(
                connect<any, any, any>(() => ({
                    pageid: page.pageId
                }))(page.component) // <-- Error on this line, on page.component.
            );
            if (Array.isArray(page.paths)) {
                // Multiple paths available for a single route. For example "/" and "/Home" for the
                // home page.
                _forEach(page.paths, inner => {
                    routes.push(<Route key={inner} path={inner} render={() => this.chooseRender(inner, Component)} />);
                });
            } else {
                // A single path for a route.
                routes.push(
                    <Route
                        key={page.paths}
                        path={page.paths}
                        render={() => this.chooseRender(page.paths as string, Component)}
                    />
                );
            }
        });
        return routes;
    }

    render() {
        return (
            <div style={{ zIndex: -1 }}>
                <IntlProvider locale="en">
                    <div>
                        <ConnectedRouter history={this._history}>
                            <Switch>{this.renderPages()}</Switch>
                        </ConnectedRouter>
                    </div>
                </IntlProvider>
            </div>
        );
    }
}

export default StandardRouter;

маршрутный object.ts

type RouteObject = {
    key: string;
    component: React.ComponentType<any>;
    paths: string | string[];
    text: string;
    shortText?: string;
    index?: number;
    pageId?: string;
    icon?: React.ReactNode;
};

export default RouteObject;

тест-route.tsx

import React from 'react';

class TestRoute extends React.PureComponent {
    render() {
        return <div />;
    }
}

export default TestRoute;

Опять же, я не уверен, почему я не могу назначить свойство PureComponent свойству ComponentType. Заранее спасибо!

Оригинальный вопрос ниже


Я работаю над преобразованием нашего javascript-приложения в TypeScript и столкнулся с проблемой. Я все еще пытаюсь понять все сложности TypeScript.

Мы динамически подключаем наши маршруты к избыточному и маршрутизатору во время выполнения. Это работает правильно, когда в JavaScript, но так как я преобразовал его в TypeScript, у меня возникли проблемы с его компиляцией. Я получаю ошибку

ОШИБКА в [at-loader] ./src/ts/components/app/StandardRouter.tsx:63:21 TS2345: Аргумент типа 'ReactNode' не может быть назначен параметру типа 'ComponentType'. Тип 'undefined' нельзя назначить типу 'ComponentType'.

Я вижу, что ошибка вызвана неверным типом, но я не уверен, почему.

StandardRouter.tsx

// React router component. Connects all routes to the application.
import * as React from 'react';
import { connect } from 'react-redux';
import { Route, Switch, withRouter } from 'react-router-dom';
import { ConnectedRouter } from 'connected-react-router';
import { IntlProvider } from 'react-intl';
import _forEach from 'lodash/forEach';
import _find from 'lodash/find';
import { History, createHashHistory } from 'history';
import { connectRouter } from 'connected-react-router';
import reducerRegistry from '../../api/private/core/reducer-registry';
import routeRegistry from '../../api/private/core/route-registry';
import RouteObject from '../../interfaces/route-object';
import TestRoute from '../test/test-route';

class StandardRouter extends React.Component {
    constructor(props: object) {
        super(props);
        this._history = createHashHistory();
        reducerRegistry.register({
            name: 'router',
            key: 'cb3dceb0-367b-4c2a-a0fb-a8d8671283e2',
            reducer: connectRouter(this._history)
        });
        routeRegistry.setChangeListener(() => {
            if (this._isMounted) {
                this.forceUpdate();
            } else if (!this._isMounted && !this._doUpdate) {
                this._doUpdate = true;
            }
        });
        routeRegistry.register({
            key: '90e8246f-96a6-49a5-920a-a2cf0d347fcd',
            component: <TestRoute />,
            paths: '/',
            text: 'Test 123'
        });
    }

    componentDidMount(): void {
        this._isMounted = true;
        if (this._doUpdate) {
            this.forceUpdate();
        }
    }

    private _history: History;
    private _isMounted: boolean = false;
    private _doUpdate: boolean = false;

    chooseRender(path: string, Comp: React.ComponentType): React.ReactNode {
        return <Comp />;
    }

    // Creates Route objects based on routes collection from registry
    renderPages(): React.ReactNode[] {
        const routes: React.ReactNode[] = [];
        const loadedRoutes: RouteObject[] = routeRegistry.getRoutes();
        _forEach(loadedRoutes, page => {
            const Component = withRouter(
                connect(() => ({
                    pageid: page.pageId
                }))(page.component) // <-- Error on this line, on page.component.
            );
            if (Array.isArray(page.paths)) {
                // Multiple paths available for a single route. For example "/" and "/Home" for the
                // home page.
                _forEach(page.paths, inner => {
                    routes.push(<Route key={inner} path={inner} render={() => this.chooseRender(inner, Component)} />);
                });
            } else {
                // A single path for a route.
                routes.push(
                    <Route
                        key={page.paths}
                        path={page.paths}
                        render={() => this.chooseRender(page.paths as string, Component)}
                    />
                );
            }
        });
        return routes;
    }

    render() {
        return (
            <div style={{ zIndex: -1 }}>
                <IntlProvider locale="en">
                    <div>
                        <ConnectedRouter history={this._history}>
                            <Switch>{this.renderPages()}</Switch>
                        </ConnectedRouter>
                    </div>
                </IntlProvider>
            </div>
        );
    }
}

export default StandardRouter;

маршрутный object.ts

type RouteObject = {
    key: string;
    component: React.ReactNode;
    paths: string | string[];
    text: string;
    shortText?: string;
    index?: number;
    pageId?: string;
    icon?: React.ReactNode;
};

export default RouteObject;

тест-route.tsx

import React from 'react';

class TestRoute extends React.PureComponent {
    render() {
        return <div />;
    }
}

export default TestRoute;

Есть ли что-то, что могло бы вызвать это? Я не уверен, почему он не использует ReactNode.

...