TypeScript + React:如何扩展JSX元素?

906 阅读2分钟

TypeScript的React类型化为所有可能的HTML元素提供了很多接口。但有时,你的浏览器、你的框架或你的代码比可能的东西要领先一点。

比方说,你想在Chrome中使用最新的图片功能,并懒散地加载你的图片。一个渐进式的增强,所以只有了解情况的浏览器才知道如何解释。其他浏览器足够强大,不会在意。

<img src="/awesome.jpg" loading="lazy" alt="What an awesome image" />

你的TypeScript JSX代码?错了。

function Image({ src, alt }) {
  // 💥 Property 'loading' does not exist...
  return <img src={src}
   alt={alt}
   loading="lazy" />
}

为了防止这种情况,我们可以用我们自己的属性扩展可用的接口。TypeScript的这一特性被称为声明合并

创建一个@types 文件夹,并在其中放置一个jsx.d.ts 文件。改变你的TypeScript配置,使你的编译器选项允许额外的类型。

{
  "compilerOptions": {
    ...
    /* Type declaration files to be included in compilation. */
    "types": ["@types/**"],
  },
  ...
}

我们重新创建确切的模块和接口结构。

  1. 该模块被称为'react'
  2. 接口是ImgHTMLAttributes<T> extends HTMLAttributes<T>

我们从原始类型中知道。在这里,我们添加我们想拥有的属性。

import 'react'

declare module 'react' {
  interface ImgHTMLAttributes<T> extends HTMLAttributes<T> {
    loading?: 'lazy' | 'eager' | 'auto';
  }
}

当我们在做的时候,让我们确保我们不会忘记alt文本!

import 'react'

declare module 'react' {
  interface ImgHTMLAttributes<T> extends HTMLAttributes<T> {
    loading?: 'lazy' | 'eager' | 'auto';
+   alt: string;
  }
}

好多了!TypeScript将采用原始定义并合并你的声明。你的自动完成可以给你所有可用的选项*,*当你忘记了一个alt文本时,会出错。

我们可以使用同样的方法,当我们希望styled-jsx ,与TypeScript兼容。style TypeScript不承认jsxglobal 标签的属性。让我们来改变这一点。

declare module 'react' {
  interface StyleHTMLAttributes<T> extends React.HTMLAttributes<T> {
    jsx?: boolean;
    global?: boolean;
  }
}

当与Preact一起工作时,事情就有点复杂了。原始的HTML类型是非常慷慨的,不像React的类型那么具体。这就是为什么我们在定义图像的时候要更明确一些。

declare namespace JSX {
  interface IntrinsicElements {
    "img": HTMLAttributes & {
      alt: string,
      src: string,
      loading?: 'lazy' | 'eager' | 'auto';
    }
  }
}

这确保了altsrc 都是可用的,并设置了可选属性loading

不过,技术是一样的。声明合并,它对命名空间、接口和模块都有效。