发布于 2026-01-06 0 阅读
0

TypeScript with React 教程 React TypeScript 学生应用开发者全球展示挑战赛,由 Mux 呈现:展示你的项目!

TypeScript 与 React 教程

React TypeScript 学生应用

由 Mux 主办的 DEV 全球展示挑战赛:展示你的项目!

React TypeScript教程

介绍

TypeScript 已发展成为 Web 开发领域最流行、应用最广泛的编程语言之一。TypeScript 是 JavaScript 的超集。简单来说,TypeScript 本质上是 JavaScript,但添加了一些额外的特性。TypeScript 的目标是编写强类型 JavaScript 代码。强类型有助于 Web 应用程序扩展,同时最大限度地减少 bug。TypeScript 代码通过 TypeScript Compiler 或 Babel 等编译器转换为 JavaScript。

为了更好地理解,我们将使用 React 和 TypeScript 开发一个学生应用程序。本教程将为你提供在下一个项目中使用 TypeScript 所需的一切。

如果您更喜欢在 YouTube 上观看此教程,请点击以下链接。

最终解决方案已在 GitHub 上提供。

GitHub 标志 tndungu / React-TypeScript-StudentApp

React TypeScript 学生应用

React TypeScript 学生应用

一款使用 React TypeScript 开发的学生应用。它包含了类型推断、类型注解、联合类型、接口、枚举和泛型等功能。

本地安装

  1. 使用以下命令克隆仓库:git clone https://github.com/tndungu/React-TypeScript-StudentApp.git
  2. 使用您喜欢的文本编辑器打开存储库。我个人偏好使用 Visual Studio Code。
  3. 打开终端并运行以下命令:npm install
  4. 使用 npm start 运行项目。这将在http://localhost:3000打开项目。

视频

YouTube 上有关于如何一步步搭建这个项目的详细教程。

React TypeScript 学生应用




先决条件

本教程假设您已具备React的基础知识。

为什么要使用 TypeScript?

使用 TypeScript 有很多好处。主要好处如下:

  • 强类型确保在开发阶段就能发现错误,而不是在应用程序上线运行后才发现。此外,它也使代码调试变得更加容易。
  • 文档 - 它为 JavaScript 代码提供文档,使其易于阅读和维护。
  • 节省开发时间。 
  • TypeScript 中的泛型提供了一个强大的类型系统,为开发者提供了很大的灵活性。

TypeScript 学生应用程序

我们将使用 React 构建一个应用程序,该应用程序将涵盖 TypeScript 的以下几个方面。

  • 道具 
  • 类型推断与类型注解
  • 联合类型
  • 组织界面
  • 枚举 
  • 仿制药

应用开发:分步指南

要启动一个新的 TypeScript 应用,请使用以下命令

  • 纱线
yarn create-react-app student-app --template typescript
Enter fullscreen mode Exit fullscreen mode
  • npm
npx create-react-app student-app --template typescript
Enter fullscreen mode Exit fullscreen mode

进入 student-app 目录,或者yarn start如果npm start使用 npm。

道具

我们将首先向<App/>组件传递一个 prop。这是一个字符串,其中包含应用程序的名称。这里我们将看到 TypeScript 的第一个用例。
修改 `<app-name>`App.tsxindex.tsx`<app-name>` 文件,使其如下所示。删除 ` App.test.tsx<app-name>` 文件。

//Index.tsx
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';

const root = ReactDOM.createRoot(
  document.getElementById('root') as HTMLElement
);
root.render(
  <React.StrictMode>
    <App name="Student App" />
  </React.StrictMode>
);

//App.tsx
export interface AppName {
  name: string;
}

function App({ name }: AppName) {
  return (
    <div className="App">
      <h1>{name}</h1>
    </div>
  );
}

export default App;
Enter fullscreen mode Exit fullscreen mode

在浏览器中,您应该可以看到学生应用。可以看到,AppName是一个接口,它有一个名为 name 的属性。 
接口是一种抽象类型,编译器使用它来确定定对象可以拥有哪些属性名称。它用于类型检查。 
AppName接口中,name 属性是一个字符串,这就是为什么我们将字符串传递给App组件的原因。如果您尝试传递任何其他类型,例如整数或布尔值,则应用会报错,甚至无法编译。如果您未使用 TypeScript,则应用可以编译,但在部署后会出现意外结果。 

类型推断与类型注解

const [studentId,setStudentId] = useState(0)

setStudentId('test')
Enter fullscreen mode Exit fullscreen mode

如果你尝试运行上面的代码,它将无法编译,TypeScript 会报错。这是因为 TypeScript 已经推断出 `x` 的类型studentId为整数(数字)。因此,如果你尝试将字符串赋值给 `x`,studentId就会在编译时产生错误。这在 TypeScript 中被称为类型推断。 

同样,下面的代码在 TypeScript 中也无法运行。

const [student, setStudent] = useState(null)

setStudent({
  name: 'Antony',
  surname: 'Ndungu',
  age: 15
})
Enter fullscreen mode Exit fullscreen mode

这是因为 TypeScript 会推断 student 对象为 null 类型,因此我们需要显式定义 student 对象,并在useState()钩子函数中告诉 TypeScript user 可以是 null 或 student 对象。这被称为类型注解。我们将使用Student接口来实现这一点。最终代码App.tsx如下所示:

import { useState } from "react";
import './App.css'

export interface AppName {
  name: string;
}

export interface Student {
  name: string;
  surname: string;
  age?: number;
  address?: {
    houseNo: number;
    street: string;
    Town: string;
  }
}

function App({ name }: AppName) {
  const [student, setStudent] = useState<Student | null>(null)

  const addStudent = () => {
    setStudent({
      name: 'Antony',
      surname: 'Ndungu',
      age: 20
    })
  }

  return (
    <div className="App">
      <h1>{name}</h1>
      <p><b>{student?.name} {student?.surname}</b></p>
      <button onClick={addStudent}> Add Student</button>
    </div>
  );
}
export default App;

Enter fullscreen mode Exit fullscreen mode

从上面的代码可以看出,学生可以是对象,也可以是null虚拟Student对象。这由代码表示useState<Student | null>(null)。这引入了另一个概念,称为联合类型

联合类型

当你的对象可以具有不同类型的情况时,就会出现这种情况。例如,你可能有一个对象,它包含多个类型。const [student, setStudent] = useState<Student | null | boolean>(null)在这种情况下,Student | null | boolean它们就是联合类型。

组织界面

就我们的接口而言,存在两个问题:

  • 我们不应该像在接口中那样嵌套对象Student。相反,我们应该为地址(Address)定义另一个接口。
  • 为了便于维护和重用,接口应该放在单独的模块中。 

我们将创建一个新的接口。然后,我们将通过在文件夹内创建一个文件并将接口移动到该文件中来Address创建一个新的接口模块。接下来,我们将在该文件中导入我们的接口。最终的文件将如下所示:interfaces.tssrcApp.tsxApp.tsxInterfaces.ts

//App.tsx
import { useState } from "react";
import './App.css'
import { Student, AppName } from './interfaces'

function App({ name }: AppName) {
  const [student, setStudent] = useState<Student | null>(null)

  const addStudent = () => {
    setStudent({
      name: 'Antony',
      surname: 'Ndungu',
      age: 20
    })
  }

  return (
    <div className="App">
      <h1>{name}</h1>
      <p><b>{student?.name} {student?.surname}</b></p>
      <button onClick={addStudent}> Add Student</button>
    </div>
  );
}

export default App;

//interfaces.tsx
export interface AppName {
  name: string;
}

export interface Address {
  houseNo: number;
  street: string;
  Town: string;
}

export interface Student {
  name: string;
  surname: string;
  age?: number;
  address?: Address
}

Enter fullscreen mode Exit fullscreen mode

枚举

枚举类型用于存储常量值。在我们的示例中,学生级别可以是“本科生”或“研究生”。

export enum Level {
  Undergraduate = "Undergraduate",
  Postgraduate = "Postgraduate"
}

Enter fullscreen mode Exit fullscreen mode

上述枚举类型可用于根据学生的年级有条件地显示学生的年龄,如下所示:

{
  student?.level === Level.Undergraduate &&
  <p><b>Age: {student.age}</b></p>
}
Enter fullscreen mode Exit fullscreen mode

仿制药

泛型是 TypeScript 的一项重要特性,用于创建可重用的组件。同一个组件可以处理不同的数据类型,如下所示。 

使用同一个组件显示学生列表和课程列表。

TypeScript 学生课程列表显示

在我们的学生应用中,我希望显示两个列表:一个用于学生列表,另一个用于课程列表。如果不使用泛型,我最终需要创建两个组件来分别显示这两个列表。但是,使用泛型后,我只需一个组件即可显示这两个列表。即使DisplayData应用规模不断扩大,该组件也可以重复使用,用于显示任何类型的列表。

我在src文件夹中创建了DisplayData.tsx一个组件。该文件内容如下:

interface Item {
  id: number;
}

interface DisplayDataItem<T> {
  items: Array<T>
}

export const DisplayData = <T extends Item>({ items }: DisplayDataItem<T>) => {
  return (
    <>
      <ul>
        {items.map((item) => (
          <li key={item.id}>{JSON.stringify(item)}</li>
        ))}
      </ul>
    </>
  )
}

Enter fullscreen mode Exit fullscreen mode

接口Item具有一个属性id,这意味着任何使用此组件的对象都必须具有该id属性。接口DisplayDataItem<T>是一个表示Array<T>数组类型的对象T,这意味着它可以被任何包含项目数组的对象使用。DisplayData是一个接受项目数组并显示列表的函数。 
以下是App.tsxApp.cssdata.ts文件的最终代码。


//App.tsx
import { useState } from "react";
import './App.css'
import { Student, AppName, Level } from './interfaces'
import { studentList, coursesList } from "./data";
import { DisplayData } from "./DisplayData";

function App({ name }: AppName) {
  const [student, setStudent] = useState<Student | null>(null)

  const addStudent = () => {
    setStudent({
      name: 'Antony',
      surname: 'Ndungu',
      age: 20,
      level: "Undergraduate"
    })
  }

  return (
    <div className="App">
      <h1>{name}</h1>
      <p><b>{student?.name} {student?.surname}</b></p>
      {student?.level === Level.Undergraduate &&
        <p><b>Age: {student.age}</b></p>
      }
      <button onClick={addStudent}> Add Student</button>
      <h3>List of Students</h3>
      <div>
        <DisplayData items={studentList} />
      </div>
      <h3>List of Courses</h3>
      <div>
        <DisplayData items={coursesList} />
      </div>
    </div>
  );
}

export default App;

//data.ts
export const studentList = [
  { id: 1, name: 'Antony', surname: 'Ndungu', level: 'Undergraduate', age: 20 },
  { id: 2, name: 'Chanelle', surname: 'John', level: 'Postgraduate', age: 50 },
  { id: 3, name: 'Ian', surname: 'Smith', level: 'Undergraduate', age: 46 },
  { id: 4, name: 'Michael', surname: 'Starke', level: 'Postgraduate', age: 64 },
  { id: 5, name: 'Chris', surname: 'De Kock', level: 'Undergraduate', age: 19 },
]

export const coursesList = [
  { id: 1, code: 'A141', name: 'Algorithms Analysis', description: 'Analysis & Design' },
  { id: 1, code: 'BN445', name: 'Computer Architecture I', description: 'Computer Architecture' },
  { id: 1, code: 'P888', name: 'Operations Research', description: 'Maths - Operations Research' },
  { id: 1, code: 'Z9989', name: 'Discrete Maths', description: 'Discrete Mathematics' }
]

.App {
  display: flex;
  width: 100%;
  align-items: center;
  justify-content: center;
  flex-direction: column;
}

li{
  list-style-type: none;
}

button {
  height: 30px;
  width: 150px;
  background-color: turquoise;
  border-radius: 5px;
}

Enter fullscreen mode Exit fullscreen mode

通用搜索功能

最后,我们将添加一个通用搜索功能,点击按钮即可根据学生姓名或年龄对学生列表进行排序。 
创建一个GenericSort.ts文件并确保包含以下代码。这段代码接收一个数组元素列表和一个排序键,然后返回排序后的列表。例如,如果我想根据学生姓名对学生列表进行排序,我会调用该函数。GenericSort(studentList,"name")
这是一个强大的泛型用例,如果我想根据不同的排序列对学生记录列表进行排序,就可以使用它。如果不使用 TypeScript 来实现此功能,最终会生成许多难以扩展的函数。

//GenericSort
export const GenericSort = <T,>(items: Array<T>, key: keyof T) => {

  items.sort((a, b) => {
    if (a[key] > b[key]) {
      return 1;
    }
    if (a[key] < b[key]) {
      return -1;
    }
    return 0;
  })
  return items
}
Enter fullscreen mode Exit fullscreen mode
//App.tsx
import { useState } from "react";
import './App.css'
import { Student, AppName, Level } from './interfaces'
import { studentList, coursesList } from "./data";
import { DisplayData } from "./DisplayData";
import { GenericSort } from "./GenericSort";

function App({ name }: AppName) {
  const [student, setStudent] = useState<Student | null>(null)
  const [list, setList] = useState(studentList)

  const addStudent = () => {

    setStudent({
      name: 'Antony',
      surname: 'Ndungu',
      age: 20,
      level: "Undergraduate"
    })
  }

  const sortData = () => {

    GenericSort(studentList, "age")
    setList([...studentList])
  }
  return (
    <div className="App">
      <h1>{name}</h1>
      <p><b>{student?.name} {student?.surname}</b></p>
      {student?.level === Level.Undergraduate &&
        <p><b>Age: {student.age}</b></p>
      }
      <button onClick={addStudent}> Add Student</button>
      <br />
      <button onClick={sortData}>Sort Data</button>
      <h3>List of Students</h3>
      <div>
        <DisplayData items={list} />
      </div>
      <h3>List of Courses</h3>
      <div>
        <DisplayData items={coursesList} />
      </div>
    </div>
  );
}

export default App;

Enter fullscreen mode Exit fullscreen mode

结论

太棒了!至此,您已经掌握了使用 TypeScript 构建 React 应用的核心基础知识。在项目的最后一部分,我们介绍了 TypeScript 的一些高级特性,例如泛型。 祝您
使用 TypeScript 编码愉快!
如果您需要任何帮助,请随时在下方留言。

文章来源:https://dev.to/tndungu/typescript-with-react-tutorial-3ah5