我读了很多书,发现了类似的东西,但不是我想要的。
我想通过使用对象内的 prop 值来定义类型,并使用该信息来定义该对象内的其他属性。
JSX.IntrinsicElements 包括所有反应元素的定义。在此示例中,让我们关注 SVG 元素 circle
和 path
。
所以让我们尝试定义类型防御:
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
值中获取这种类型。
我只想提一下,使用 const test: svgCustomType<"path"> = {...}
会起作用,但不会接受 cx
。
为了清楚:
我不确定在打字稿中是否可能。
我正在写一个 react lib,我希望我的用户能够在打字时获得智能感知建议,如果用户运行 typescript(而不是 javascript),他也会收到类型错误。
假设我的 lib 组件有类型为 svgHeadProp
的 prop svgCustomType
。
所以我希望用户能够写:
svgHeadProp={svgElem:"path",svgProps:{...}}
而不是:
svgHeadProp<"path">={svgElem:"path",svgProps:{...}}
因为并非我的所有用户都使用 typescript(对于那些使用 typescript 的用户,为 prop 指定类型很烦人)
答案 0 :(得分:3)
问题是要在类型签名中使用 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
属性需要正确键入的值,可以替代使用:
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>
},
})
更新:也可以将 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
相同。