具有高阶函数的泛型

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 我添加了一个示例,说明如何将其传递给渲染函数。

以上是关于具有高阶函数的泛型的主要内容,如果未能解决你的问题,请参考以下文章

选择Typescript的原因:支持定义执行时的泛型参数

Scala快速入门--异常处理泛型高阶函数隐式转换

Kotlin学习

Javascript中具有递归的高阶函数

C++ 高阶操作:模板元编程

Kotlin小知识之高阶函数