【问题标题】:Extending React components in TypeScript在 TypeScript 中扩展 React 组件
【发布时间】:2015-09-30 12:47:49
【问题描述】:

我正在使用带有 TypeScript 的 React.js。有什么方法可以创建继承自其他组件但具有一些额外道具/状态的 React 组件?

我想要实现的是这样的:

interface BaseStates {
    a: number;
}

class GenericBase<S extends BaseStates> extends React.Component<void, S> {
    protected getBaseInitialState(): BaseStates {
        return { a: 3 };
    }
}

class Base extends GenericBase<BaseStates> {
    getInitialState(): BaseStates {
        return super.getBaseInitialState();
    }
}

interface DerivedStates extends BaseStates {
    b: number;
}

class Derived extends GenericBase<DerivedStates> {
    getInitialState(): DerivedStates {
        var initialStates = super.getBaseInitialState() as DerivedStates; // unsafe??
        initialStates.b = 4;
        return initialStates
    }
}

但是,如果我在 Derived 中调用 this.setState,这将失败,我收到 TypeScript 错误(DerivedStates 类型的参数不可分配给 S 类型)。我想这不是 TypeScript 特有的东西,而是将继承与泛型混合的一般限制(?)。是否有任何类型安全的解决方法?

更新

我确定的解决方案(基于 David Sherret 的回答):

interface BaseStates {
    a: number;
}

class GenericBase<S extends BaseStates> extends React.Component<void, S> {
    constructor() {
        super();
        this.state = this.getInitialState();
    }

    getInitialState(): S {
        return { a: 3 } as S;
    }

    update() {
        this.setState({ a: 7 } as S);
    }
}

interface DerivedStates extends BaseStates {
    b: number;
}

class Derived extends GenericBase<DerivedStates> {
    getInitialState(): DerivedStates {
        var initialStates = super.getInitialState();
        initialStates.b = 4;
        return initialStates;
    }

    update() {
        this.setState({ a: 7, b: 4 });
    }
}

【问题讨论】:

  • 我也很难找到答案。基本上我想要的是将A&lt;DerivedProps&gt; 组件传递给A&lt;BaseProps&gt; 接口,以在A 内部和外部进行一些基本的道具类型检查。
  • 但后来我发现情况类似于将DerivedProps =&gt; void分配给BaseProps =&gt; void。第一个函数(或类构造函数)不能继承第二个。
  • 考虑使用abstract 类/函数。 (see answer below)

标签: oop generics inheritance reactjs typescript


【解决方案1】:

您可以使用类型断言在Derived 中一次设置状态的几个属性:

this.setState({ b: 4 } as DerivedStates); // do this
this.setState({ a: 7 } as DerivedStates); // or this
this.setState({ a: 7, b: 4 });            // or this

顺便说一句,getInitialState 不需要使用不同的名称...你可以这样做:

class GenericBase<S extends BaseStates> extends React.Component<void, S> {
    constructor() {
        super();        
        this.state = this.getInitialState();
    }

    protected getInitialState() {
        return { a: 3 } as BaseStates as S;
    }
}

class Derived extends GenericBase<DerivedStates> {
    getInitialState() {
        var initialStates = super.getInitialState();
        initialStates.b = 4;
        return initialStates;
    }
}

【讨论】:

  • 感谢您的快速回答。对于您的解决方案,构造函数的第二行出现错误:type 'BaseStates' is not assignable to type 'S' 这并不奇怪,因为SBaseStates 的子类。
  • @Thegaram 我只是在解决这个问题。现已修复。
  • 谢谢!仍然不确定这些低调有多安全,但我的问题得到了解答。
  • 你知道为什么它不适用于可选状态吗?如果我将a 设置为可选,在GenericBase.getInitialState 我得到一个错误:neither type '{ a: number; }' nor type 'S' is assignable to the other
  • @Thegaram 我必须更明确地让它工作:return { a: 3 } as BaseStates as S。有点烦人,但也许是有原因的。我更新了答案。
【解决方案2】:
import { Component } from 'react'


abstract class TestComponent<P = {}, S = {}, SS = any> extends Component<P, S, SS> {
  abstract test(): string
}


type Props = {
  first: string,
  last: string,
}

type State = {
  fullName: string,
}

class MyTest extends TestComponent<Props, State> {
  constructor(props: Props) {
    super(props)
    this.state = {
      fullName: `${props.first} ${props.last}`
    }
  }

  test() {
    const { fullName } = this.state
    return fullName
  }
}

【讨论】:

    猜你喜欢
    • 2020-10-13
    • 2020-08-12
    • 2020-06-03
    • 1970-01-01
    • 2021-08-10
    • 2021-03-24
    • 1970-01-01
    • 2012-09-29
    • 2019-12-18
    相关资源
    最近更新 更多