跳至主要內容

JSX

樱桃茶大约 23 分钟

JSX

JSX 是 TypeScript 中的一个扩展,它允许你在 TypeScript 文件中写类似 HTML 的语法。这种语法被用来描述用户界面,主要被 React 和一些其他框架所使用。理解 JSX 对于开发现代 web 应用尤其重要。

  • 基本概念:

    • JSX 通过特殊的编译步骤转换成标准的 JavaScript 对象和函数调用。
    • 虽然看起来很像 HTML,但实际上 JSX 支持任何 JavaScript 表达式。大括号 {} 内可以嵌入变量、表达式等。
  • 元素类型:

    • 内置元素(比如 <div><span>)对应 HTML 元素。
    • 自定义组件 以大写字母开头,比如 <MyComponent />,表示一个 React 组件或其他支持 JSX 的框架的自定义组件。
  • 属性:

    • 类似 HTML 属性,但遵循 camelCase 命名法(例如:className 而不是 class)。
    • 表达式可以通过大括号 {} 插入到属性中。
    • 特殊属性 keyref 在 React 等框架中有特别的意义。
  • JSX 儿子:

    • 可以包含子元素,文本内容,以及其他 JavaScript 表达式。
    • 子元素也可以是组件,这允许构建复杂的组件树结构。
  • 类型断言:

    • 当使用 TypeScript 编写 JSX 时,你可能需要告诉编译器元素的确切类型,这可以通过类型断言实现。
    • 例子:<MyComponent /> as MyComponentType
  • 泛型组件:

    • TypeScript 支持在 JSX 中使用泛型,这样可以提高组件的复用性和类型安全。
    • 例子:<MyComponent<string> /> 表示 MyComponent 组件接受 string 类型的泛型参数。

示例:

假设我们有一个 React 组件 WelcomeMessage,它接收一个名为 name 的 prop:

interface WelcomeMessageProps {
  name: string;
}

function WelcomeMessage({ name }: WelcomeMessageProps) {
  return <h1>Hello, {name}!</h1>;
}

在父组件中使用它:

const App = () => {
  return <WelcomeMessage name="Alice" />;
};

以上是 JSX 在 TypeScript 中使用的一个简单例子。通过这个例子,你可以看到如何定义组件,如何传递和接收 props,以及 JSX 让你能够以声明式的方式构建 UI。

基本用法

JSX 是一种 TypeScript 和 JavaScript 文件中用来描述 UI 结构的语法扩展。它看起来很像 HTML,但实际上是在 JavaScript 代码中直接写的。在 React 等库中,JSX 被用来创建元素和组件。

基本用法包括以下几个要点:

  • 元素类型: JSX 的标签决定了元素的类型。

    • <div></div> 创建一个div类型的 React 元素。
    • <MyComponent></MyComponent> 创建一个名为MyComponent的自定义组件实例。
  • 嵌入表达式: 可以在 JSX 中通过大括号{}嵌入任何有效的 JavaScript 表达式。

    • <div>{1 + 1}</div> 将显示2
    • const name = 'World'; <div>Hello, {name}!</div> 将显示Hello, World!
  • 属性: JSX 里可以使用属性,类似于 HTML 中的属性。

    • <a href="https://example.com">Link</a> 添加了一个href属性到a元素。
    • <MyComponent color="blue" />MyComponent传递了一个名为color的 props,值为"blue"
  • 子元素: JSX 标签内部的内容会成为其子元素。

    • <ul><li>First Item</li><li>Second Item</li></ul> 创建了一个列表,其中有两个列表项。
  • 布尔值与空值: 如果你想传递一个true的布尔值作为 props,可以直接写属性名。

    • <MyComponent isEnabled /> 等同于 <MyComponent isEnabled={true} />
    • 如果你不给属性赋值,它默认为true
  • 表达式作为子元素: 如果表达式计算出的是一个数组或者元素序列,它们将成为子元素集合。

    • {[<li key="A">First</li>, <li key="B">Second</li>]} 将渲染两个列表项。

使用 JSX 时需要注意:

  • 必须导入 React,因为 JSX 最终会被转译成React.createElement调用。
  • 标签名使用驼峰命名法,例如onClick而不是onclick
  • 自闭合标签必须使用/>结束,例如<img src="image.jpg" />
  • 默认情况下,只能返回单个根节点。如果想返回多个节点,它们必须被包裹在一个元素(如<div>)或片段(<>...</>)中。
  • TypeScript 在处理 JSX 时,会要求定义组件的类型信息以确保正确性。

as 操作符

JSX 中的 as 操作符用于告诉 TypeScript 编译器该如何解析 JSX 元素内部的特定表达式。在某些情况下,我们需要覆盖它的默认行为或类型判断,这时,我们就可以使用 as 关键字。

  • 在 TypeScript 中,当你使用 JSX 时,默认情况下会有一个对元素类型的推断。例如,<div/> 会被推断为 JSX.Element 类型。
  • 有时候,你可能需要明确地指定一个不同的类型,这时你可以使用 as 来进行类型断言,这样 TypeScript 就知道那个元素具体应该是什么类型。
  • 使用 as 操作符可以确保在编译时不会发生意料之外的类型错误,并且能够使你的代码更清晰和易于维护。

下面是一些例子:

// 假设你有一个MyComponent组件,它有特定的类型定义
(<MyComponent />) as MySpecialComponentType;
// 当你需要将一个通用的JSX元素断言为更加具体的类型时
const element = (<div>Some content</div>) as HTMLElement;
// 在泛型组件中使用as操作符来断言特定的泛型类型
function GenericComponent<T>(props: T) {
  // 组件逻辑...
}

const componentInstance = <GenericComponent<{ foo: string }>> as ReactElement<{ foo: string }>;

请注意,过度使用 as 操作符可能会绕过编译器的类型检查,因此建议只在真正需要时才使用它。同时,如果你对基础的 JavaScript 和 JSX 熟悉的话,理解 TypeScript 中的 as 操作符也会容易很多,因为它类似于其他语言中的类型转换机制。

类型检查

在 TypeScript 中,JSX 是一种嵌入式的 XML-like 语法。它允许你在 TypeScript 文件中写 HTML 结构的代码,这种方式常见于 React 和其他前端框架中。当我们在使用 JSX 时,TypeScript 提供了强大的类型检查功能,以确保你在编写组件时能够安全且准确地传递数据。下面将解释 JSX 的类型检查相关知识点:

  • 基本元素: TypeScript 可以检查 JSX 中的基本 HTML 元素。例如,如果你有一个<input>标签,并且给它传递了一个不存在的属性,TypeScript 会提示错误。

    // 正确示例
    const input = <input type="text" />;
    
    // 错误示例:value 属性不存在于 button 上。
    // TypeScript 将报错
    const button = <button value="click me">Click Me</button>;
    
  • 属性类型检查: 在定义组件时,你可以指定一个接口来约束 props 的类型,这样传递给组件的属性就会进行类型检查。

    interface ButtonProps {
      label: string;
    }
    
    const Button = (props: ButtonProps) => <button>{props.label}</button>;
    
    // 正确传递
    const myButton = <Button label="Click Me" />;
    
    // 错误传递: 'lable' 属性不存在,应为 'label'
    // TypeScript 将报错
    const anotherButton = <Button lable="Click Here" />;
    
  • 子元素类型检查: 针对组件的子元素,TypeScript 也能进行类型检查。通过定义 children 的类型,你可以控制组件内部能包含哪些类型的子元素。

    interface CardProps {
      children: React.ReactNode; // 使用 React 的类型定义子元素
    }
    
    const Card = ({ children }: CardProps) => <div>{children}</div>;
    
    // 正确用法
    const myCard = (
      <Card>
        <h2>Title</h2>
        <p>Description here...</p>
      </Card>
    );
    
    // 如果 Card 组件的定义不包括特定类型的 children,
    // TypeScript 将根据实际情况报错
    
  • 泛型组件: 在创建可复用的组件时,可以利用 TypeScript 的泛型来增加组件的灵活性。这样你可以在组件内部对 props 或 state 进行更精细的类型控制。

    interface GenericListProps<T> {
      items: T[];
      renderItem: (item: T) => React.ReactNode;
    }
    
    function GenericList<T>({ items, renderItem }: GenericListProps<T>) {
      return <div>{items.map(renderItem)}</div>;
    }
    
    // 使用
    const numbers = [1, 2, 3];
    const listItems = (
      <GenericList items={numbers} renderItem={(item) => <div>{item}</div>} />
    );
    

这些是 TypeScript 在处理 JSX 时进行类型检查的一些关键方面。通过利用 TypeScript 的类型系统,你可以创建更健壯、易于维护的前端应用。

固有元素

在 TypeScript 中,JSX 是一种语法扩展,允许我们像编写 HTML 一样,在 TypeScript 文件中编写类似 XML 的结构。当我们使用 React 或与 JSX 兼容的其它库时,这个功能变得尤为重要。

固有元素 指的是直接写在 JSX 中的 HTML 标签(如<div>, <span>, <a>等),与自定义组件(比如<MyComponent />)相对。在类型检查上,TypeScript 会检查这些元素是否正确使用了合适的属性,并且会确保没有传入不存在的属性。

  • 类型检查固有元素时,TypeScript 会参照 DOM API 的类型定义。例如,一个 <div> 标签接受 className 属性而不是 class,因为在 JSX 中使用 class 可能与 JavaScript 中的类关键词冲突。

    // 正确的用法
    const element = <div className="container"></div>;
    
    // 错误的用法,TypeScript将报错因为 'class' 在这里不是一个有效属性
    const wrongElement = <div class="container"></div>;
    
  • TypeScript 还会检查属性值的类型。例如,如果你传给一个 HTML 元素的属性应该是一个函数,但你提供了一个字符串,TypeScript 会抛出错误。

    // 正确的用法
    const button = (
      <button onClick={() => console.log("Clicked!")}>Click me</button>
    );
    
    // 错误的用法,TypeScript将报错因为 onClick 应该是一个函数
    const wrongButton = (
      <button onClick="console.log('Clicked!')">Click me</button>
    );
    
  • 当你想使用非标准属性时,你可以通过 TypeScript 的类型扩展来告诉 TypeScript 接受这些属性。这通常是通过声明一个全局接口并扩展 JSX.IntrinsicElements 实现的。

    // 扩展全局的 JSX.IntrinsicElements 接口
    declare global {
      namespace JSX {
        interface IntrinsicElements {
          "my-cool-element": any; // 允许 <my-cool-element> 使用任何属性
        }
      }
    }
    
    // 现在这个自定义元素就不会导致类型错误
    const customElement = (
      <my-cool-element some-attribute="value"></my-cool-element>
    );
    

理解并利用 TypeScript 中的 JSX 类型检查能力可以帮助你避免许多常见的错误,并确保你的组件按预期方式使用固有元素。通过类型检查,你可以更加信心地编写代码,并享受到 IDE 提供的自动完成和错误提示等功能。

基于值的元素

在 TypeScript 的上下文中,当你使用 JSX 时,你会遇到两种类型的元素: 基于值的元素和基于类型的元素。我们来专注于“基于值的元素”。

  • 基于值的元素是指那些最终会编译为某个变量或者表达式的 JSX 元素。
  • 在 JSX 中,元素也就是组件,它们可以是函数或者类,而这些函数或类在编译后会被视为值。
  • 类型检查器需要去检查这个值是否可以赋给 JSX 元素,以及这个值的所有属性是否与组件的属性类型兼容。

以下是一些例子:

  1. 函数组件:

    interface Props {
      message: string;
    }
    
    const MyComponent: React.FC<Props> = (props) => <div>{props.message}</div>;
    
    // 正确使用
    <MyComponent message="Hello World" />
    
    // 错误使用,因为缺少了message属性
    <MyComponent />
    
  2. 类组件:

    interface Props {
      message: string;
    }
    
    class MyComponent extends React.Component<Props> {
      render() {
        return <div>{this.props.message}</div>;
      }
    }
    
    // 正确使用
    <MyComponent message="Hello Class Component" />
    
    // 错误使用,因为message属性的类型不符
    <MyComponent message={42} />
    
  3. 当你定义一个 JSX 元素时,TypeScript 会根据当前作用域内可用的值来确定其类型。如果该值为一个函数或类(即 React 组件),则 TypeScript 将检查你提供给该组件的 props 是否符合组件的属性类型定义。

  4. 如果你的项目中启用了--jsx标志,并设置为react,TypeScript 将编译 JSX 语法为React.createElement调用。在这种情况下,对于基于值的元素,它会检查React.createElement的参数类型是否正确。

总结起来,基于值的元素在类型检查层面确保你传递给组件的 props 或属性符合期望的类型,有助于预防运行时错误并提高代码的健壮性。

无状态函数组件

无状态函数组件 (Stateless Function Components) 是 React 中的一种组件,它们不管理状态(即不使用 this.state),只是接收 props 并返回应该渲染的内容。在 TypeScript 中,这些组件同样可以进行类型检查和智能提示。

  • 基本概念:

    • TypeScript 允许你指定组件的 props 类型,使得传递给组件的属性是可预测并且类型安全的。
    • 无状态组件通常通过箭头函数或者普通函数来创建。
  • 类型注解:

    • 你可以为函数组件的 props 设置一个接口 (interface) 或类型别名 (type),然后在定义组件时将其作为参数类型。
  • 示例:

    // 定义 Props 的类型
    interface MyComponentProps {
      message: string;
      count?: number; // 可选属性
    }
    
    // 无状态函数组件
    const MyComponent: React.FC<MyComponentProps> = ({ message, count = 0 }) => {
      return (
        <div>
          {message} {count}
        </div>
      );
    };
    
    // 使用组件并传入 props
    const element = <MyComponent message="Hello" count={42} />;
    
    • 在这个例子中,如果尝试给 <MyComponent /> 传递一个不存在于 MyComponentProps 接口的属性,TypeScript 编译器将会抛出错误。
    • 另外,如果有必填属性未被传入,也会得到编译错误。
  • 默认 props:

    • 可以为组件的 props 设定默认值,这样即使在使用时没有提供某些可选属性,组件也能正常工作。
    • 在上面的例子中,count 属性就有一个默认值 0
  • 类型推断:

    • TypeScript 能够根据给定的 props 类型自动推断组件内部使用的 props 类型,从而帮助避免在实现组件逻辑时发生类型错误。
  • 泛型组件:

    • 如果需要创建可复用的组件,可使用泛型来定义组件的 props 类型,使组件更加灵活。
  • 利用React.FC:

    • React.FC (或 React.FunctionComponent)是一个泛型类型,用来明确地标记一个无状态函数组件,并且包含 children 等默认的类型定义。
    • 使用 React.FC 时,你不需要显式声明 children prop,因为它已经包含在 React.FC 类型中了。

总结:在 TypeScript 中使用无状态函数组件可以增强代码的健壮性和可维护性,通过准确的类型注解,开发者能够更容易地理解组件接受哪些 props 以及各个 prop 的类型,同时编辑器和编译器也能够提供更好的支持。

类组件

在 TypeScript 的上下文中,处理 JSX 时,你会遇到两种类型的元素:基于值的元素和基于类型的元素。当我们谈论类组件时,我们通常指的是 React 组件体系中的一个方面。

  • 基于值的元素:这些元素是指那些像变量一样可以被赋值和引用的 JSX 元素。

    • 例如,当你定义了一个 React 组件类,你实际上创建了一个可以被 JSX 引用的“值”。
  • 类组件

    • 类组件是基于 ES6 的类定义的 React 组件。
    • 当你在 JSX 中使用这样的类组件时,TypeScript 需要确保你传递给组件的属性(props)符合该类组件期望接收的属性类型。

假设有如下类组件:

import React from "react";

interface MyComponentProps {
  name: string;
}

class MyComponent extends React.Component<MyComponentProps> {
  render() {
    return <div>Hello, {this.props.name}</div>;
  }
}
  • 在这个例子中,MyComponent是一个类组件,它期望接收一个对象作为 props,这个对象具有一个名为name的属性,且类型为string

  • 当你在 JSX 中使用MyComponent时,TypeScript 将会检查你提供的 props 是否匹配MyComponentProps接口定义。

const el = <MyComponent name="Alice" />; // 正确用法

const wrongEl = <MyComponent name={42} />; // 错误用法,因为'name'应该是一个字符串
  • 在错误的用法中,尝试将数字42赋值给属性name,而不是一个字符串,TypeScript 会提示类型错误,因为它根据类的 props 接口来检查类型。

通过这种方式,TypeScript 帮助确保你正确地使用组件,并且传递的 props 类型是正确的。这在大型项目中非常有价值,因为它减少了运行时出现属性类型错误的风险。

属性类型检查

TypeScript 中的 JSX 支持允许你在 TypeScript 项目中使用 JSX 语法,这是一种嵌入类 XML 的语法到 JavaScript 代码中的方式。当你使用 JSX 时,很可能你也会在使用 React 或者其它支持 JSX 的库。在这些库中,你经常需要传递属性(props)给自定义的组件或者原生的 HTML 元素。属性类型检查就是确保你传递给组件的属性符合期望的类型。

  • 属性类型检查:
    • TypeScript 可以检查 JSX 元素的属性是否接收了正确的类型。
    • 对于原生 HTML 元素,TS 会检查是否所有属性都按照预期地传递给了正确的元素,并且以正确的类型。
    • 对于用户自定义的组件,属性类型检查由接口或者类型别名决定。
    • 如果一个属性是可选的,TS 会确保这个属性要么被省略,要么就应该是正确的类型。
    • 如果一个属性被标记为必需的,TS 会确保你必须提供这个属性,并且它的类型是正确的。

例如,假设你有一个 React 组件<MyComponent>,它接受一个名为name的字符串属性和一个名为age的数字属性:

interface MyComponentProps {
  name: string;
  age?: number; // 可选属性
}

const MyComponent: React.FC<MyComponentProps> = ({ name, age }) => {
  // ...
};

当你使用这个组件时,TypeScript 会这样进行类型检查:

// 正确的用法
<MyComponent name="Alice" age={25} />

// 错误的用法:'name'属性缺失
<MyComponent age={25} />

// 错误的用法:'age'的类型不正确(应该是一个数字)
<MyComponent name="Bob" age="Thirty" />

以上代码展示了如何通过接口定义组件所期待接收的属性以及如何进行属性类型检查。如果提供了错误的类型或者省略了必需的属性,TypeScript 编译器将会发出警告。这样可以帮助开发者在开发过程中捕获潜在的错误,确保组件按照预期工作。

子孙类型检查

在 TypeScript 中使用 JSX 时,子孙类型检查(Descendant Type Checking)是一个特定的概念,涉及到如何在组件树中传递和验证组件的属性类型。当你用 TypeScript 编写 React 或类似的库代码时,这个特性尤其重要。这里,我们将通过一些简单的例子来理解它。

  • 基本概念:在 JSX 中,组件可以嵌套其他组件。每个组件都可能接受不同的 props(属性)。TypeScript 允许你为这些 props 定义类型,确保在组件间传递数据时类型正确。

  • 例子 1:假设有一个WelcomeMessage组件,它接受一个名为name的 prop,类型为string

interface WelcomeProps {
  name: string;
}

function WelcomeMessage(props: WelcomeProps) {
  return <h1>Hello, {props.name}</h1>;
}

当你尝试将该组件嵌入到另一个组件中时,TypeScript 会检查传给WelcomeMessagename属性是否符合string类型。

  • 例子 2:考虑一个复杂点的场景,你有一个UserList组件,它渲染了多个UserItem组件,并且每个UserItem都需要一个user对象作为 prop。
interface User {
  id: number;
  name: string;
}

interface UserItemProps {
  user: User;
}

function UserItem(props: UserItemProps) {
  return <li>{props.user.name}</li>;
}

interface UserListProps {
  users: User[];
}

function UserList(props: UserListProps) {
  return (
    <ul>
      {props.users.map((user) => (
        <UserItem key={user.id} user={user} />
      ))}
    </ul>
  );
}

在这个例子中,TypeScript 会帮助确保你为每个UserItem传递的user对象符合User接口的结构,同时也帮助保证你传递给UserListusers数组由符合User接口的对象组成。

通过这些例子,我们看到 TypeScript 的类型系统如何帮助我们确保在组件树中正确地传递 props,减少在运行时遇到的问题。子孙类型检查让开发者在构建大型应用时能更容易地管理和维护代码,因为任何不匹配的类型都会在编译时被捕获,而不是运行时。这就是 TypeScript 在 JSX 中提供的强大功能之一。

JSX 结果类型

JSX 结果类型涉及在 TypeScript 中使用 JSX 时,如何定义和处理生成的元素。JSX 是 JavaScript XML 的缩写,它允许你将像 HTML 一样的语法直接写在 JavaScript 代码中,这通常用在 React 等库中来描述用户界面。

  • 当你在 TypeScript 文件中使用 JSX,编译器需要知道如何解释这些标记。
  • 为了让 TypeScript 理解这些 JSX 表达式应该被转换成什么,你需要告诉 TypeScript 每个 JSX 元素的返回类型。

例子:

// 如果你在React中使用TypeScript,那么JSX元素默认是React元素类型
const element: JSX.Element = <div>Hello, TypeScript with JSX!</div>;

但是,有些情况下,你可能不是在使用 React,或者你想要自定义 JSX 转换逻辑,这时候就可以自定义 JSX 结果类型:

  • .tsx 文件的顶部,你可以定义一个全局的命名空间 JSX,其中包含了自定义的元素类型。
  • Element 接口在这个命名空间内定义了 JSX 表达式的返回类型。

自定义结果类型例子:

declare namespace JSX {
  interface IntrinsicElements {
    foo: any;
  }

  interface Element {
    render: () => void;
  }
}

// 这里 `<foo />` 将会被认为是一个拥有 `render` 方法的对象
const myElement: JSX.Element = <foo />;
myElement.render();

在上面的例子中:

  • 定义了一个新的 JSX 元素 <foo />
  • 并指定了所有 JSX 元素应该具有的类型,即具有一个 render 函数。

通过这种方式,你可以灵活地告诉 TypeScript 你的 JSX 元素将会是什么样的对象,并且如何处理它们。这对于集成 TypeScript 到不同的 JSX 使用场景(比如除了 React 之外的库或框架)非常重要。

嵌入的表达式

嵌入的表达式

  • TypeScript 结合 JSX 时,允许你在 JSX 代码中嵌入表达式。这些表达式写在花括号{}中。
  • 当 JSX 被编译成 JavaScript 后,这些嵌入的表达式会被计算出结果,并且嵌入到生成的 JavaScript 代码中。

例子:

  1. 变量嵌入: 可以将 TypeScript 变量嵌入到 JSX 元素中。这对于动态更新 UI 非常有用。

    const name = "小明";
    const greeting = <h1>你好, {name}!</h1>;
    

    上面的代码中,{name}是一个嵌入的表达式,它会被替换成变量name的值。

  2. 表达式计算: 除了直接嵌入变量,你还可以在 JSX 中嵌入任何有效的 JavaScript 表达式。

    const element = <h1>{1 + 1}</h1>;
    

    这里,{1 + 1}被计算为 2,然后嵌入到<h1>标签中。

  3. 条件渲染: 嵌入表达式也可以用来根据条件渲染不同的元素或内容。

    const isLoggedIn = true;
    const userGreeting = <h1>{isLoggedIn ? "欢迎回来!" : "请登录"}</h1>;
    

    在这个例子中,根据isLoggedIn变量的值,将渲染不同的文本。

  4. 循环渲染: 可以使用数组的.map()方法结合嵌入的表达式,在 JSX 中进行循环渲染。

    const items = ["苹果", "香蕉", "橘子"];
    const itemList = (
      <ul>
        {items.map((item) => (
          <li key={item}>{item}</li>
        ))}
      </ul>
    );
    

    这段代码会为items数组中的每一项生成一个<li>元素。

通过这些例子可以看到,嵌入的表达式极大地增强了 JSX 的灵活性和表现力,使得在 TypeScript 中使用 JSX 来构建动态用户界面变得更加简单和直观。

React 整合

JSX > React 整合

  • JSX 是 JavaScript 的语法扩展,允许在 JavaScript 代码中编写看起来像 HTML 的结构。
  • TypeScript 支持 JSX,并且可以和 React(一个流行的前端库)很好地集成。
  • 使用 TypeScript 编写 React 组件时,你可以利用 TypeScript 的类型检查来增强组件的稳定性和可维护性。

例子:定义一个简单的 React 组件并为其添加类型

import React from "react";

// 定义 Props 的类型
interface WelcomeProps {
  name: string;
}

// 使用箭头函数定义组件,指定 Props 的类型
const Welcome: React.FC<WelcomeProps> = (props) => {
  return <h1>Hello, {props.name}</h1>;
};

export default Welcome;

// 在其他组件中使用 Welcome 组件时,必须提供一个 name 属性,类型为 string
  • .tsx 文件中,你可以像写普通 HTML 那样编写 JSX,并且利用 TypeScript 的类型检查。
  • TypeScript 要求你定义组件的属性(Props)和状态(State)的类型,这有助于防止运行时错误。

例子:在组件中使用 State 和事件处理

import React, { useState } from "react";

interface CounterProps {
  initialCount: number;
}

const Counter: React.FC<CounterProps> = ({ initialCount }) => {
  // 使用泛型定义 state 的类型
  const [count, setCount] = useState<number>(initialCount);

  const handleIncrement = () => {
    setCount(count + 1);
  };

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={handleIncrement}>Click me</button>
    </div>
  );
};

export default Counter;

// 当使用 Counter 组件时,你必须提供 initialCount 属性,类型为 number
  • 当你将 TypeScript 和 React 结合使用时,有一些特别的类型和接口来帮助你定义组件的 props、state 和事件处理器。

例子:为事件处理器定义类型

import React from "react";

interface ButtonProps {
  label: string;
  onClick: React.MouseEventHandler<HTMLButtonElement>; // 定义点击事件处理器的类型
}

const Button: React.FC<ButtonProps> = ({ label, onClick }) => {
  return <button onClick={onClick}>{label}</button>;
};

export default Button;

// 这样在使用 Button 组件时,onClick 属性需要传递一个符合 React.MouseEventHandler 类型的函数
  • TypeScript 的泛型以及特定的类型如 React.FC, React.MouseEventHandler 等,都是用来确保在创建复杂组件时既保持了灵活性又增加了类型安全性。

工厂函数

JSX 是 TypeScript 与 React 在一起使用时常见的语法扩展,它允许你在 JavaScript 代码中书写类似 HTML 的标记。当编译这些 JSX 代码时,需要将 JSX 转换成有效的 JavaScript 代码。这里就需要用到所谓的“工厂函数”。

  • 工厂函数是一个用来创建 React 元素的函数。当你使用 JSX 时,每个 JSX 元素最终都会通过调用某个工厂函数转换成普通的 JavaScript 对象。

  • 默认情况下,当你使用 JSX 时,React 会假定你准备使用 React.createElement 作为工厂函数。但是,如果你想使用自定义的工厂函数,则需要在 TypeScript 中进行配置。

  • 为了指定使用自定义工厂函数,你可以在 tsconfig.json 文件中设置 "jsxFactory" 选项,指向你选择的工厂函数。例如:

    {
      "compilerOptions": {
        "jsx": "react",
        "jsxFactory": "myCustomFactoryFunction"
      }
    }
    
  • 一旦你指定了一个自定义工厂函数,所有的 JSX 代码都会用这个函数来创建元素。例如,如果你有以下 JSX 代码:

    const element = <div>Hello, world!</div>;
    

    它将被编译成如下 JavaScript(假设 myCustomFactoryFunction 是你指定的工厂函数):

    const element = myCustomFactoryFunction("div", null, "Hello, world!");
    
  • 这给予你灵活性去自定义如何处理 JSX,例如,在一个不同的库中创建视图元素或者注入特定的行为。

  • 如果你使用了像 Preact 这样的 React 替代品,或者有其他渲染逻辑的需求,这种自定义工厂函数的能力将非常有用。

  • 除了 "jsxFactory" 之外,TypeScript 也支持 "jsxFragmentFactory" 选项,允许你指定用于编译 JSX 片段(即 <></><React.Fragment></React.Fragment>)的工厂函数。

  • 记住,当你使用自定义工厂函数时,你需要确保该函数在运行时可用,并且其生成的元素与你的渲染环境兼容。