具有高阶函数的泛型
Posted
技术标签:
【中文标题】具有高阶函数的泛型【英文标题】:Generics with Higher Order Function 【发布时间】:2020-03-11 08:32:30 【问题描述】:我想为我现有的React
之一创建一个返回构造函数的函数
成分。返回值是传入的函数组件的自定义类扩展。
换句话说:
我的高阶函数withObservableStream()
的返回值——它返回另一个
function - 采用现有的React
组件(具有类型化的道具)并返回新组件的构造函数
最终应呈现为DOM
。因为我只是扩展传入的组件
RxJS
订阅两个组件将共享相同的道具。
类型系统应该:
投诉属性不匹配我怎样才能做到这一点 - 谁能向我展示“泛型”的魔力?这可能吗?
type Props = Triggers & InitialState;
function App(props: Props)
return <button
onClick=(event) =>
props.onClick(event);
>props.text (state: props.counterX)</button>;
// still all good here:
const appInstance = App(...triggers, text: "init", counterX: -1);
const WrappedApp = withObservableStream(
interval(5000).pipe(
map((o) => (counterX: o)),
),
...triggers,
// counterX: -1,
text: "XXX",
,
)(App);
// type sytem should complain meaningful here:
// 2nd & 3rd parameter of observableStream() should match the constructor signature of App (type Props)
// for both new WrappedApp(...) and render(<WrappedApp...)
stackblitz 上的完整代码示例。
【问题讨论】:
【参考方案1】:我修改了您的代码以添加所需的类型限制。
基本上你需要将泛型类型添加到withObservableStream
的返回函数中:
withObservableStream.tsx
import React from "react";
import Observable from "rxjs";
export default <T extends Observable.Observable<any>, U, V>(observable: T, triggers: U, initialState: V) =>
type cState = U & V;
return function(Component: React.ComponentType<cState>)
return class extends React.Component<cState, V>
private subscription: any;
constructor(props: cState)
console.log("got props", props);
super(props);
this.state =
...initialState,
;
console.log(this.state);
public componentDidMount()
console.log("Subscribing...");
this.subscription = observable.subscribe((newState) =>
console.log("setting a new state...", newState);
this.setState( ...newState );
console.log("state: ", this.state);
);
public componentWillUnmount()
console.log("Unsubscribing...");
this.subscription.unsubscribe();
public render()
return (
<Component ...this.props ...this.state ...triggers />
);
;
;
index.tsx
import React from "react";
import render from "react-dom";
import interval from "rxjs";
import map from "rxjs/operators";
import withObservableStream from "./withObservableStream";
type Triggers =
onClick(event: React.MouseEvent<htmlButtonElement>): void,
;
type InitialState =
text: string,
counterX: number,
;
type Props = Triggers & InitialState;
export function App(props: Props)
return <button
onClick=(event) =>
props.onClick(event);
>props.text (state: props.counterX)</button>;
const triggers: Triggers = onClick: () => console.log("clicked!!!") ;
const appInstance = App(...triggers, text: "init", counterX: -1);
render(appInstance, document.getElementById("root1"));
const WrappedApp = withObservableStream(
interval(5000).pipe(
map((o) => (counterX: o)),
),
...triggers,
counterX: -1,
text: "XXX",
,
)(App);
const appInstance2 = new WrappedApp(...triggers, counterX: -1, text: "23");
render(<WrappedApp ...triggers counterX=-1 text="23"/>, document.getElementById("root1"))
document.getElementById("root2"));
在此处查看运行代码:stackblitz。
【讨论】:
太棒了,看起来很有希望!你有什么建议我可以将WrappedApp
的实例传递给render()
吗? const appInstance = new WrappedApp(...); render(appInstance, ...);
请直接将相关信息放入答案中;外部链接会随着时间的推移而腐烂。请参阅How to Answer 中的“始终引用重要链接的最相关部分,以防目标站点无法访问或永久脱机”。
@YannicHamann 我添加了一个示例,说明如何将其传递给渲染函数。以上是关于具有高阶函数的泛型的主要内容,如果未能解决你的问题,请参考以下文章