reactjs - Typescript - 从对象 Prop 值推断泛型类型

标签 reactjs typescript

我读了很多,发现了类似的东西,但不是我想要的。

我想通过在对象内使用 prop 值来定义类型,并使用该信息来定义该对象内的其他属性。

JSX.IntrinsicElements 包括所有反应元素的定义。让我们关注 SVG 元素 circlepath对于这个例子。

让我们尝试定义类型定义:

export type svgCustomType<T extends "path" | "circle"> = {
  svgElem: T;
  svgProps?: { [key in keyof JSX.IntrinsicElements[T]]: JSX.IntrinsicElements[T] };
};

这里我尝试获取类型值 T(可以是“路径”或“圆”)并使用该值为 svgProps 定义适当的属性类型.(可以是 React.SVGProps<SVGCircleElement>React.SVGProps<SVGPathElement>

示例用法:

const test: svgCustomType = {  //Error: "Generic type 'svgCustomType' requires 1 type argument(s)", why?
  svgElem: "path",
  svgProps: {
    cx: 10,  // next step: should be error here because cx is not defined in `SVGPathElement`
  },
};

为什么我会得到 Generic type 'svgCustomType' requires 1 type argument(s) ?我想从 svgElem 获取这种类型值(value)。

我只想提到使用 const test: svgCustomType<"path"> = {...}将工作和cx将不被接受。

明确:
我不确定在 typescript 中是否有可能。
我正在写 react lib我希望我的用户能够在打字时获得 IntelliSense 建议,如果用户运行 typescript (而不是 javascript),他也会收到类型错误。
假设我的 lib 组件有 prop svgHeadProp类型为 svgCustomType .
所以我希望用户能够写:

svgHeadProp={svgElem:"path",svgProps:{...}}

而不是:

svgHeadProp<"path">={svgElem:"path",svgProps:{...}}

因为不是我所有的用户都使用 typescript(对于那些这样做的用户,它烦人的为 prop 指定类型)

最佳答案

问题在于使用 SvgCustomTypeGeneric在类型签名中,您必须传递该类型参数。使用 SvgCustomTypeGeneric<'path' | 'circle'>不起作用,因为这只会允许 T 的每次出现成为 'path''circle'它们之间没有任何依赖关系。但是,联合类型会起作用,SvgCustomTypeGeneric<'path'> | SvgCustomTypeGeneric<'circle'> ,可以从 'path' | 'circle' 创建工会。

为了展示它是如何工作的,让我们将通用函数重命名为 SvgCustomTypeGeneric , 并使用 JSX.IntrinsicElements[T]作为 svgProps 的类型(否则这些值是嵌套的,例如 svgProps: cx: {cx: 10}) ):

type SvgKey = 'path' | 'circle'

export type SvgCustomTypeGeneric<T extends SvgKey> = {
  svgElem: T;
  svgProps?: JSX.IntrinsicElements[T]
}

创建联合类型 SvgCustomTypeGeneric类型,您可以使用映射类型创建一个对象,每个键对应的 SvgCustomTypeGeneric输入,然后从中提取值:

type SvgCustomType = {[K in SvgKey]: SvgCustomTypeGeneric<K>}[SvgKey]
//  evaluates to: SvgCustomTypeGeneric<"path"> | SvgCustomTypeGeneric<"circle">

测试时,cx属性没有帮助,因为 SVGPathElement 上也允许使用它。 ,但 ref property 需要一个正确键入的值,并且可以用来代替:

const test: SvgCustomType[] = [{
    svgElem: 'path',
    svgProps: {
      cx: 10, // No error, SVGPathElement can have 'cx' properties.
      ref: createRef<SVGPathElement>(),
    },
  }, {
    svgElem: 'circle',
    svgProps: {
      cx: 10,
      ref: createRef<SVGCircleElement>(),
    },
  }, { // ERROR
    svgElem: 'circle',
    svgProps: {
      cx: 10,
      ref: createRef<SVGPathElement>(),
    },
  },
]

另一种解决方案是创建一个通用构造函数来验证对象。它更简单一些,并提供更漂亮的错误消息,但确实需要添加实际代码而不仅仅是类型:

const mkSvg =<K extends SvgKey>(x: SvgCustomTypeGeneric<K>) => x

const test = mkSvg({
  svgElem: 'circle',
  svgProps: {
    ref: createRef<SVGPathElement>(),
    // Type 'RefObject<SVGPathElement>' is not assignable to type 'LegacyRef<SVGCircleElement> 
  },
})

TypeScript playground

更新:也可以写成 SvgCustomType作为单线,通过使用条件类型来引入类型变量并将其分布在联合上:

export type SvgCustomTypeOneLiner =
  SvgKey extends infer T ? T extends SvgKey ? {
    svgElem: T;
    svgProps?: JSX.IntrinsicElements[T]
  } : never : never

如果你真的想全力以赴,你甚至可以放弃对 SvgKey 的依赖。 :

type SvgCustomTypeUltimate = 
  keyof JSX.IntrinsicElements extends infer T ? T extends keyof JSX.IntrinsicElements ? {
    svgElem: T;
    svgProps?: JSX.IntrinsicElements[T]
  } : never : never

这两种类型的行为与 SvgCustomType 相同。上面定义的。

TypeScript playground

关于reactjs - Typescript - 从对象 Prop 值推断泛型类型,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/67193871/

相关文章:

javascript - <div> 不能作为 <p> 的后代出现

visual-studio - TypeScript/Visual Studio 2012/编译参数

javascript - Angular 2-zone.js :355 Unhandled Promise rejection: Template parse errors: 'stocks' is not a known element:

typescript - typescript 参数名称中的问号是什么

javascript - 如何在 react-scripts v3 中使用 redux-promise-middleware

reactjs - 如何在 React 或 React-Redux 中将数据从一个组件传递到另一个组件?

javascript - 如何在正在测试的父函数中模拟子函数的输出?

javascript - 将组件链接到单独的元素,并相对于该元素显示它

angular - amcharts 堆叠条形图 - 响应事件突出显示值(更改 alpha)

android - react native 构建失败