【问题标题】:Typescript: React render using external function which accepts component instance打字稿:使用接受组件实例的外部函数反应渲染
【发布时间】:2019-03-18 06:45:54
【问题描述】:

我正在创建一堆内部包含一些常用功能的组件,其中一个是处理渲染的组件。简化后如下所示:

const render = (
    instance: React.Component<{}, {flag: boolean}>,
    cb: () => React.ReactNode
) => instance.state.flag ? cb() : '';

class Test extends React.Component<{}, {flag: boolean}> {
    state = {flag: true};
    render() { return render(this, () => '') }
}

不过,TypeScript 对此设置不满意,并表示:'render' implicitly has return type 'any' because it does not have a return type annotation and is referenced directly or indirectly in one of its return expressions. 就我个人而言,我看不到此参考。

作为一个可能的线索,如果我这样重写这段代码:

const render = (
    state: {flag: boolean},
    cb: () => React.ReactNode
) => state.flag ? cb() : '';

class Test extends React.Component<{}, {flag: boolean}> {
    state = {flag: true};
    render() { return render(this.state, () => '') }
}

...一切都很好,但我真的很想使用组件本身,因为现在还没有看到一些复杂性。

有什么问题?我可以用这个通用函数做点什么,让 TypeScript 不争论吗?


UPD:一些测试让我创建了一个更接近所需示例的示例:

type Component<T> = React.Component<{}, {flag: T}>
const render = <T>(
    instance: Component<T>,
    cb: () => React.ReactNode
) => instance.state.flag ? cb() : '';

class Test extends React.Component<{}, {flag: boolean}> {
    state = {flag: true};
    render() {
        const result = render(this, () => '');
        return result;
    }
    inner = () => 'test';
}

这里的重点是,外部render 函数是泛型的,而instance 类型依赖于泛型参数,因此TypeScript 无法用适当的参数代替T。是否可以在不丢失类型安全的情况下帮助他?

【问题讨论】:

    标签: reactjs typescript type-inference


    【解决方案1】:

    这里的问题似乎与渲染函数中实例参数的类型推断有关。

    让我们看看

    • 即使您将实例类型标记为 React.Component,当 typescript 尝试推断渲染类型时,它也必须使用必须是 React.Component 的“this”实例,为此它必须检查渲染类成员也是,然后重新开始。所以我想打字稿决定放弃这个推论
    type RenderF = (
        instance: React.Component<any, any>,
        cb: () => React.ReactNode) => React.ReactNode;
    
    const render: RenderF = (
        instance: React.Component<{}, {flag: boolean}>,
        cb: () => React.ReactNode
    ) => instance.state.flag ? cb() : null;
    
    class Test extends React.Component<{}, {flag: boolean}> {
        state = {flag: true};
        render() { return render(this, () => null) }
    }
    
    • 因此,一个简单的解决方案是将 RenderF 实例参数标记为 any,以避免 typescript 推断再次检查实例。
    type RenderF = (
        instance: any, // <-----
        cb: () => React.ReactNode) => React.ReactNode;
    
    const render: RenderF = (
        instance: React.Component<{}, {flag: boolean}>,
        cb: () => React.ReactNode
    ) => instance.state.flag ? cb() : null;
    
    class Test extends React.Component<{}, {flag: boolean}> {
        state = {flag: true};
        render() { return render(this, () => null) }
    }
    

    UPD

    如果您不想在实例参数中失去类型安全性,您可以只在新类型中指定您需要的内容

    interface FlagComponent {
        state: { flag: boolean }
    }
    
    type RenderF = (
        instance: FlagComponent,
        cb: () => React.ReactNode) => React.ReactNode;
    
    const render: RenderF = (
        instance: React.Component<{}, {flag: boolean}>,
        cb: () => React.ReactNode
    ) => instance.state.flag ? cb() : null;
    
    class Test extends React.Component<{}, {flag: boolean}> {
        state = {flag: true};
        render() { return render(this, () => null) }
    }
    

    【讨论】:

    • 谢谢,最后一个成功了。我要补充一点,这看起来像是 Pick&lt;Component&lt;T&gt;, 'state'&gt; 或类似的工作。
    猜你喜欢
    • 1970-01-01
    • 2020-08-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-04-17
    • 1970-01-01
    • 2021-12-04
    • 2018-06-25
    相关资源
    最近更新 更多