javascript - 如何在传递道具时使用 TypeScript 实现“as”道具?
问题描述
我正在构建一个组件库,我需要其中一些组件具有可自定义的标签名称。例如,有时看起来像 a<button>
实际上是 a <a>
。所以我希望能够像这样使用按钮组件:
<Button onClick={onClick}>Click me!</Button>
<Button as="a" href="/some-url">Click me!</Button>
理想情况下,我希望根据“as”道具推断可用道具:
// Throws an error because the default value of "as" is "button",
// which doesn't accept the "href" attribute.
<Button href="/some-url">Click me!<Button>
我们可能还需要传递一个自定义组件:
// Doesn't throw an error because RouterLink has a "to" prop
<Button as={RouterLink} to="/">Click me!</Button>
这是没有 TypeScript 的实现:
function Button({ as = "button", children, ...props }) {
return React.createElement(as, props, children);
}
那么,如何在传递道具时使用 TypeScript 实现“as”道具?
注意:我基本上是在尝试做什么styled-components
。但是我们正在使用 CSS 模块和 SCSS,所以我负担不起添加样式组件。不过,我对更简单的选择持开放态度。
解决方案
新答案
我最近看到了 Iskander Samatov 的文章React polymorphic components with TypeScript,其中分享了一个更完整、更简单的解决方案:
import * as React from "react";
interface ButtonProps<T extends React.ElementType> {
as?: T;
children?: React.ReactNode;
}
function Button<T extends React.ElementType = "button">({
as,
...props
}:
ButtonProps<T>
& Omit<React.ComponentPropsWithoutRef<T>, keyof ButtonProps<T>>
) {
const Component = as || "button";
return <Component {...props} />;
}
旧答案
我花了一些时间研究样式化组件的类型声明。我能够提取最低要求的代码,这里是:
import * as React from "react";
import { Link } from "react-router-dom";
type CustomComponentProps<
C extends keyof JSX.IntrinsicElements | React.ComponentType<any>,
O extends object
> = React.ComponentPropsWithRef<
C extends keyof JSX.IntrinsicElements | React.ComponentType<any> ? C : never
> &
O & { as?: C };
interface CustomComponent<
C extends keyof JSX.IntrinsicElements | React.ComponentType<any>,
O extends object
> {
<AsC extends keyof JSX.IntrinsicElements | React.ComponentType<any> = C>(
props: CustomComponentProps<AsC, O>
): React.ReactElement<CustomComponentProps<AsC, O>>;
}
const Button: CustomComponent<"button", { variant: "primary" }> = (props) => (
<button {...props} />
);
<Button variant="primary">Test</Button>;
<Button variant="primary" to="/test">
Test
</Button>;
<Button variant="primary" as={Link} to="/test">
Test
</Button>;
<Button variant="primary" as={Link}>
Test
</Button>;
我从 styled-components 中删除了很多比这更复杂的东西。例如,他们有一些解决方法来处理我删除的类组件。所以这个片段可能需要针对高级用例进行定制。
推荐阅读
- python - python中带有日期时间库的if语句
- reactjs - 创建项目构建时 React 的导入文件如何工作
- vue.js - Vue3 js 中的 2 个 css 文件(阿拉伯文和英文 css 文件)之间如何切换?
- kotlin - Kotlin:将由括号分隔的对列表解析为 Pairs() 列表
- c# - 如何在遗留类中使用 DI 容器来创建新类的实例
- javascript - 在 URL 中设置字符串(Reactjs)
- visual-studio-code - 使用带有 Visual Studio 代码断点的函数框架 python 进行调试
- spring-boot - 使用 Java Faker 引发依赖错误的 Spring Boot
- mysql - 从备份中恢复 MySQL 的脚本
- java - 如何在多个平台上管理 Maven 依赖项?