当接口公开一个泛型时,只需将其用于键入一个属性即可。有没有一种方法可以基于用例的推论来“使用”?
看看这个:
请在此处假定不能简单地应用泛型,并且
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
。
答案 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
通用,您可以同时创建div
和a
框:
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