【问题标题】:Interface infer type based on a property it defines接口根据其定义的属性推断类型
【发布时间】:2020-03-06 03:35:08
【问题描述】:

当一个接口公开一个简单地用来输入属性的泛型时。有没有办法根据用例的推断来“使用”它?

看看这个:

请在此假设不能简单地应用 Generic,并且 onClick 将存在。

如您所见,我的TestObjectis 属性是一个泛型,它是静态给定的。有没有办法对此进行限制,所以当onClick 想要一个参数时,它知道is 属性是div,因此只允许value == 'div'

我的用例是在 React 世界中,我希望为我的组件提供一个定义其渲染 (createElement) 的道具,但需要它对于它应用的所有处理程序和属性是类型安全的。我想泛型会起作用,但是当发送到 forwardRef 时就会崩溃。

这是我目前拥有的一个例子,也是我的困境所在。

import { AllHTMLAttributes, createElement, forwardRef } from 'react';

interface Props<Element extends keyof JSX.IntrinsicElements>
    extends Omit<AllHTMLAttributes<Element>, 'width' | 'height'> {
    is?: Element;
    className?: string;
}

// There is a little more going on inside the Component, but you get the gist.
const Box = forwardRef<HTMLElement, Props<'div'>>(({ is, children }, ref) =>
    createElement(is, {
        ref,
    }, children));

从那里可以看到,现在 is 属性被锁定为 div

【问题讨论】:

    标签: reactjs typescript


    【解决方案1】:

    TS 目前不支持arbitrary generic value types,通用函数除外。此外,在像const x 这样的变量赋值中,编译器无法自动推断T 的类型参数。

    换句话说,你必须给TestObject一个具体的类型参数:const x: TestObject&lt;"div"&gt;。当没有指定任何内容时,您的案例仍然可以编译,因为使用了给定的默认 "div"|"a" for T。或者,您可以使用工厂函数来初始化x,但为了简单起见,我只使用前者。


    React.forwardRef 的问题与上述主题有关,虽然有点复杂。

    React.forwardRef cannot output a generic component 与当前的 React 类型定义 - 我在链接的答案中提到了一些解决方法。最简单的解决方法是使用类型断言:

    const Box = forwardRef<HTMLElement, Props<keyof JSX.IntrinsicElements>>(({ is, children }, ref) =>
      is === undefined ? null : createElement(is, { ref, }, children)) as
      <T extends keyof JSX.IntrinsicElements>(p: Props<T> &
      { ref?: Ref<HTMLElementFrom<T>> }) => ReactElement | null
    
    // this is just a helper to get the corresponding HTMLElement, e.g. "a" -> HTMLAnchorElement
    type HTMLElementFrom<K extends keyof JSX.IntrinsicElements> = 
      NonNullable<Extract<JSX.IntrinsicElements[K]["ref"], React.RefObject<any>>["current"]>
    type AnchorEle = HTMLElementFrom<"a"> // HTMLAnchorElement
    

    这将使您的Box 通用,您可以同时创建diva 框:

    const aRef = React.createRef<HTMLAnchorElement>()
    const jsx1 = <Box is="a" ref={aRef} onClick={e =>{}} />
    // is?: "a" | undefined, ref: RefObject<HTMLAnchorElement>, onClick?: "a" callback
    
    const divRef = React.createRef<HTMLDivElement>()
    const jsx2 = <Box is="div" ref={divRef} onClick={e =>{}} />
    // is?: "div" | undefined, ref: React.RefObject<HTMLDivElement>, onClick?: "div" callback
    

    Sample

    【讨论】:

      猜你喜欢
      • 2020-08-30
      • 1970-01-01
      • 1970-01-01
      • 2021-12-20
      • 1970-01-01
      • 1970-01-01
      • 2021-06-28
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多