我正在尝试在React中编写一个抽象的基础组件类,该基础类接受子类的通用属性和状态类型P
和S
,然后传递{{ 1}}和P & PBase
放入S & SBase
的模板类型中。我可以做到,但是随后调用Component
只能在this.setState()
和null
上使用。
这是一个简化的示例:
{}
据我所知,问题是编译器无法肯定/**
* Simplified version of Component<P, S>
*/
class Component<S = {}> {
setState<K extends keyof S>(state: Pick<S, K> | S | null): void {
// set the state
};
state?: S;
}
interface SBase {
x: number;
}
/**
* My abstract base class
*/
abstract class MyComponentBase<S> extends Component<S & SBase> {
doSomethingThatSetsState() {
// fails: Type 1 is not assignable to type S["x"]
this.setState({ x: 1 });
}
}
不包含名为S
的属性,而x
并不包含x
。
我认为的解决方案是强制子类的泛型不与基类的接口共享任何属性。我编写了一些帮助程序类型,如果正在比较的两种类型具有相同的属性,它们将返回number
:
never
更改上面的示例,以使用它们:
// returns types that T and S both extend
type Same<T, U> = T extends U ? T : never;
// returns never if T and S share any properties in common, else returns T
type NoneInCommon<T, S> = Same<keyof T, keyof S> extends never ? T : never;
// returns T & S if no properties are in common, else never
type Intersect<T, S> = NoneInCommon<T, S> extends never ? never : T & S;
现在的问题是它可以编译:
/**
* My abstract base class
*/
abstract class MyComponentBase<S> extends Component<Intersect<S, SBase>> {
doSomethingThatSetsState() {
// now this succeeds
this.setState({ x: 1 });
}
}
它确实使/**
* My component class
*/
class MyComponent extends MyComponentBase<SBase> {}
的{{1}}类型为MyComponent
的{{1}}属性,所以虽然有些理想,但还是不理想。有没有办法强迫编译器识别state
是无效类?