基于接口定义的属性的接口推断类型

时间:2020-03-06 03:35:08

标签: reactjs typescript

当接口公开一个泛型时,只需将其用于键入一个属性即可。有没有一种方法可以基于用例的推论来“使用”?

看看这个:

enter image description here

请在此处假定不能简单地应用泛型,并且onClick将存在。

如您所见,我的is的{​​{1}}属性是一个泛型,是静态给出的。有没有办法解决这个问题,所以当TestObject需要一个参数时,它知道onClick属性是is,因此只允许div

我的用例是在React世界中,我希望为我的组件提供一个定义其渲染(value == 'div')的prop,但是需要对它适用的所有处理程序和属性都是类型安全的。我想一个泛型可以工作,但是在发送到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

1 个答案:

答案 0 :(得分:1)

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

换句话说,您必须给TestObject一个具体的类型参数:const x: TestObject<"div">。您的情况仍会编译,因为在未指定任何内容的情况下,使用了"div"|"a"的给定默认值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