TypeScript + React:强制该组件返回另一种类型的组件

2024-04-19

假设您有一个名为的通用组件<Banner />:

function Banner({ message }: { message: string; }) {
  return <div>{message}</div>;
}

然后假设我想创建<SuccessBanner /> and <ErrorBanner />:

function SuccessBanner() {
  return <Banner message="success" />;
}

function ErrorBanner() {
  return <Banner message="error" />;
}

如何使用打字稿强制 SuccessBanner 和 ErrorBanner 返回<Banner />没有别的什么?即像这样

function SuccessBanner(): <Banner /> {
  // return <div />; // would throw a TS error
  return <Banner message="success" />;
}

function ErrorBanner(): <Banner /> {
  return <Banner message="error" />;
}

这不是 (现在 https://github.com/microsoft/TypeScript/issues/21699) 可能的。 JSX 的返回类型始终是JSX.Element,非泛型类型。


解释

The JSX命名空间提供类型和接口,告诉 TS 如何解释 JSX:存在哪些元素;哪些属性 和 可以与其各自的类型一起传递;组件函数和类的形状;并且,这里最相关的是,的返回类型JSX 工厂函数.

JSX 工厂函数

tsc有一个名为jsxFactory https://www.typescriptlang.org/tsconfig#jsxFactory这需要一个string。在反应中,这被设置为React.createElement(这也恰好是该属性的默认值)。该属性给出了在使用 JSX 时调用的函数的名称。 JSX 实现者可以利用它来提供自定义 JSX 工厂函数,如下所示:

// Example for using JSX to create DOM elements:
function jsx(tag: JSX.Tag | JSX.Component, 
             attributes: { [key: string]: any } | null, 
             ...children: Node[]) 
{
    // Handle components:
    if (typeof tag === 'function') {
        return tag(attributes ?? {}, children);
    }
    type Tag = typeof tag;
    const element: HTMLElementTagNameMap[Tag] = document.createElement(tag);

    // ... here we assign properties and add children ...
    
    return element;
}

然后我们要做的就是设置"jsxFactory": "jsx"在我们的 tsconfig 中并导入jsx在任何 .tsx 文件中(另请参阅:不带 React 的 Typescript JSX https://stackoverflow.com/questions/41557309/typescript-jsx-without-react/68238924).

所以,看起来我们应该能够在我们的泛型上进行一些类型推断jsx函数来获取特定的返回类型,对吗?

JSX.Element

错误的。不幸的是,Typescript 采取了一些捷径(避免由于深层嵌套 JSX 导致编译器速度减慢); Typescript 没有使用工厂函数推断 JSX 的返回类型,而是使用JSX.Element类型。这种类型是not通用:它可以是任何一件事:

namespace JSX {
  type Element = Node;
}

那么,React 的定义是什么JSX.Element?

namespace JSX {
    interface Element extends React.ReactElement<any, any> { }
    // ...

所以每JSX.ElementReact.ReactElement<any, any>,无论元素或组件。

在您的示例中,我们可以像这样验证这一点:

type BannerType = ReturnType<typeof Banner>;
// Intellisense: type BannerType = JSX.Element

const div = <div />
type OtherType = typeof div;
// Intellisense: type OtherType = JSX.Element

注意:在撰写本文时,Typescript 的版本是 4.6.2。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

TypeScript + React:强制该组件返回另一种类型的组件 的相关文章

随机推荐

  • 多线程 - 在数组中我应该保护什么?

    我正在编写一些具有全局数组的代码 该数组可以由两个线程访问以进行读写目的 读取或写入一系列索引时不会进行批处理 因此我试图弄清楚是否应该锁定整个数组或仅锁定我当前正在使用的数组索引 最简单的解决方案是将数组视为 CS 并在其周围放置一个大锁
  • 异步获取多个json文件后触发回调

    我有来自旧论坛的 3 个 JSON 文件 其中包含 成员 主题和回复 现在我想通过 javascript jquery 获取 3 个 json 文件来将其呈现在网站上 我可以通过首先获取成员 返回时获取主题以及返回时获取回复来同步完成此操作
  • 使用 PHP 进行安全 FTP 连接

    我有一个带有登录详细信息的安全 FTP 服务器 我正在尝试使用 PHP 连接到该安全 FTP 服务器ftp ssl connect ftp connect and ftp login函数并将所有参数正确传递给该函数 但我惊讶地发现它没有连接
  • 无法在当前状态下启动设备:正在创建

    我在 iOS 8 模拟器的 Xcode 6 中运行应用程序时遇到错误 该错误指出 无法在当前状态下启动设备 正在创建 我尝试重置模拟器的内容 另外 我在安装 Xcode 6 后重新启动了 mac 可能需要修复什么 当我将 Xcode 应用程
  • 没有安排分支的构建

    我刚刚从詹金斯开始 我正在尝试在分支 主控上运行构建 我得到的只是没有安排分支的构建 master 这是日志 Started by timer Sun Mar 05 18 23 43 NPT 2017 Starting branch ind
  • Python Reportlab 中的动态帧大小

    我尝试生成一个发货清单报告实验室 questions tagged reportlab在Python中 我试图使用 Platypus 将所有部分 如发件人地址 收件人地址 表格 放在适当的位置Frames 第一个问题我遇到的问题是我需要很多
  • UICollectionView:不同大小的项目不会在重复使用的项目上计算

    我有一个包含不同项目大小的集合视图 我在其中声明 CGSize collectionView UICollectionView collectionView layout UICollectionViewLayout collectionV
  • 在 Perl 中使用引用指向滑动窗口数组

    这是我的问题 我有 2 个数组 一种是字符数组 代表滑动窗口 角色从开头移动并推到结尾 我想使用第二个数组来存储对数组切片的引用 这些数组切片 跟随 字符移动 例子 my char array h e l l o w o r l d my
  • 表达式树不能包含赋值运算符?

    如何增加 linq 语句中的索引值 int headIndex 1 int itemIndex 1 lst from xx in db vwCustomizationHeaders where xx ProductID pID select
  • Amazon CloudFront 与 S3 --> 按域限制访问?

    在 Amazon S3 上 您可以按域限制对存储桶的访问 但据我从一位有用的 StackOverflow 用户那里了解到 您无法在 CloudFront 上执行此操作 但为什么 如果我是正确的 CloudFront 只允许基于时间的限制或
  • 使用d3.js实现元素的过渡效果

    我正在将一个元素从一个点移动到另一个点 但具体来说我想实现这个动画 http carto net svg samples path animation svg http carto net svg samples path animatio
  • Android WorkManager Worker 无法使用 Dagger Hilt `@WorkerInject` 注入

    我正在尝试遵循以下指南https developer android com training dependency injection hilt jetpack workmanager https developer android co
  • 在 Sybase ASE 中更新插入(更新或插入)?

    我正在编写一个应用程序 将数据从 Oracle 移动到 Sybase 并且需要执行更新 插入操作 在 Oracle 中 我会使用 MERGE INTO 但它似乎在 Sybase 中不可用 无论如何 在 ASE 中也不可用 我知道这可以通过多
  • 为什么 ASP.NET Dynamic 控件即使添加到 Page_Load 中也能保留 ViewState?

    我做了一些与动态控件和 ViewState 相关的研究 我读到 为了保留动态控件的 ViewState 您必须将其添加到 Page Init 事件中 这是有道理的 因为 PageLifeCycle 是 初始化 加载视图状态 加载回发数据 L
  • dynamoDB 如何存储数据?

    由于Dynamodb以键值对的形式存储数据 其中键是主键的类型 值是与其关联的数据 我想知道dynamo db是否真正理解值 json 我所说的值是指json与键关联的对象 RDBMS 中的一行 dynamo db 是否理解有一些属性以及它
  • 如何在现有高流量网站上进行facebook审核流程?

    I have 已经实现 Facebook 登录 注册在网站上 使用 Javascript 和 PHP SDK 的组合 我记得审核过程大约花了一两天的时间 那是一个全新的网站 现在 我需要将相同的功能添加到另一个现有的且目前每天有数百名访问者
  • 如何使用 devtoolset-8-gcc 安装 gcc8

    我使用的是 CentOS Linux 版本 7 3 1611 其中安装了 gcc 4 8 5 20150623 我正在寻找一种安装较新版本的 gcc 的方法 特别是 8 1 我找到了以下关于如何安装 gcc v7 的网站link 1 htt
  • 结合使用 `mutate_at` 和 `na_if` 仅将某些列的零替换为 NA

    我的数据采用以下格式 library tidyverse df lt mtcars df lt df gt mutate vs doubled vs 2 gt select mpg cyl vs am vs doubled head df
  • Socket ReceiveAsync 合并数据包

    我打算通过套接字接收数据包 但由于它们是从发送方以高频率发送的 因此其中许多数据包被打包成一个byte array SocketAsyncEventArgs Buffer然后保存多个数据包 即使它们是单独发送的 使用验证wireshark
  • TypeScript + React:强制该组件返回另一种类型的组件

    假设您有一个名为的通用组件