代码之家  ›  专栏  ›  技术社区  ›  Karol Majewski

反应组件类型差异和可分配性

  •  0
  • Karol Majewski  · 技术社区  · 7 年前

    上下文

    我有两个部分。一个组件的道具继承自另一个组件的道具。

    declare namespace Props {
      interface Fruit {
        price: number;
      }
    
      interface Banana extends Fruit {
        curvature: number;
      }
    }
    
    declare const Fruit: React.FC<Props.Fruit>;
    declare const Banana: React.FC<Props.Banana>;
    

    事实是:

    • React.FC React定义为道具的功能。
    • 我们知道函数类型的返回类型是协变的,参数类型是逆变的。 Source

    问题

    现在,在TypeScript中尝试将一个分配给另一个时 3.4.0-rc 我们得到:

    /**
     * ✅ Compile-time error. Function arguments are contravariant.
     */
    const fruit: typeof Fruit = Banana;
    
    /**
     * ✅ No error. Function arguments are contravariant.
     */
    const banana: typeof Banana = Fruit;
    
    /**
     * ✅ No errors as expected.
     */
    const one: React.ReactComponentElement<typeof Fruit> = <Fruit price={3} />;
    
    /**
     * ⁉️ No errors (but expected one). Now it's covariant.
     */
    const two: React.ReactComponentElement<typeof Fruit> = <Banana price={3} curvature={15} />
    
    /**
     * ⁉️ No errors (but expected one). Now it's contravariant.
     */
    const three: React.ReactComponentElement<typeof Banana> = <Fruit price={3} />
    
    /**
     * ✅ No errors as expected.
     */
    const four: React.ReactComponentElement<typeof Banana> = <Banana price={3} curvature={15} />
    

    问题

    为什么 two three 不会导致错误吗?

    0 回复  |  直到 7 年前
        1
  •  3
  •   hasparus    7 年前

    避免JSX和使用React。createElement解决了这个问题。

    declare namespace Props {
      interface Fruit {
        price: number;
      }
    
      interface Banana extends Fruit {
        curvature: number;
      }
    }
    
    declare const Fruit: React.FC<Props.Fruit>;
    declare const Banana: React.FC<Props.Banana>;
    
    // ✅ 
    const one: React.ReactComponentElement<typeof Fruit> = React.createElement(
      Fruit,
      { price: 1000 }
    );
    
    // ✅
    const two: React.ReactComponentElement<typeof Banana> = React.createElement(
      Banana,
      { price: 1000, curvature: 12 }
    );
    
    /**
     * ✅ Compile-time error.
     * Type 'FunctionComponentElement<Banana>' is not assignable to type
     * 'ReactComponentElement<FunctionComponent<Fruit>, Pick<PropsWithChildren<Fruit>, "price" | "children">>'.
     * ...
     * fruits.tsx(10, 5): 'curvature' is declared here.
     */
    const three: React.ReactComponentElement<typeof Fruit> = React.createElement(
      Banana
    );
    
    /**
     * ✅ Compile-time error.
     * Type 'FunctionComponentElement<Fruit>' is not assignable to type
     * 'ReactComponentElement<FunctionComponent<Banana>,
     * ...
     * Types of property 'propTypes' are incompatible. 🤔
     */
    const four: React.ReactComponentElement<typeof Banana> = React.createElement(
      Fruit,
      { price: 12 }
    );
    

    但它创造了另一个-- we lose autocomplete VSCode中的道具。

        2
  •  0
  •   Devin Rhode    5 年前

    这不是一个答案,但这可能对某些方法有用。(我不想在工作中迷失方向的正在进行的工作) git stash )

    // "React Create Element"
    type RCE<
      HTMLNodeType extends HTMLElement = HTMLElement
    > = React.DetailedReactHTMLElement<
      React.HTMLAttributes<HTMLNodeType>,
      HTMLNodeType
    >
    type RCE_string<
      HTMLNodeType extends 'div' ? HTMLDivElement : HTMLElement
    > = React.DetailedReactHTMLElement<
      React.HTMLAttributes<HTMLNodeType>,
      HTMLNodeType
    >
    const t: RCE<HTMLDivElement> = <div />
    const d: RCE<'div'> = <div />
    
    const elementTypeMap ={
        Anchor: HTMLAnchorElement,
        Area: HTMLAreaElement,
        Audio: HTMLAudioElement,
        Base: HTMLBaseElement,
        Body: HTMLBodyElement,
        BR: HTMLBRElement,
        Button: HTMLButtonElement,
        Canvas: HTMLCanvasElement,
        Data: HTMLDataElement,
        DataList: HTMLDataListElement,
        Dialog: HTMLDialogElement,
        Div: HTMLDivElement,
        DList: HTMLDListElement,
        Embed: HTMLEmbedElement,
        FieldSet: HTMLFieldSetElement,
        Form: HTMLFormElement,
        Heading: HTMLHeadingElement,
        Head: HTMLHeadElement,
        HR: HTMLHRElement,
        Html: HTMLHtmlElement,
        IFrame: HTMLIFrameElement,
        Image: HTMLImageElement,
        Input: HTMLInputElement,
        Mod: HTMLModElement,
        Label: HTMLLabelElement,
        Legend: HTMLLegendElement,
        LI: HTMLLIElement,
        Link: HTMLLinkElement,
        Map: HTMLMapElement,
        Meta: HTMLMetaElement,
        Object: HTMLObjectElement,
        OList: HTMLOListElement,
        OptGroup: HTMLOptGroupElement,
        Option: HTMLOptionElement,
        Paragraph: HTMLParagraphElement,
        Param: HTMLParamElement,
        Pre: HTMLPreElement,
        Progress: HTMLProgressElement,
        Quote: HTMLQuoteElement,
        Slot: HTMLSlotElement,
        Script: HTMLScriptElement,
        Select: HTMLSelectElement,
        Source: HTMLSourceElement,
        Span: HTMLSpanElement,
        Style: HTMLStyleElement,
        Table: HTMLTableElement,
        TableCol: HTMLTableColElement,
        TableDataCell: HTMLTableDataCellElement,
        TableHeaderCell: HTMLTableHeaderCellElement,
        TableRow: HTMLTableRowElement,
        TableSection: HTMLTableSectionElement,
        Template: HTMLTemplateElement,
        TextArea: HTMLTextAreaElement,
        Title: HTMLTitleElement,
        Track: HTMLTrackElement,
        UList: HTMLUListElement,
        Video: HTMLVideoElement,
        WebView: HTMLWebViewElement
    }