TypeScript 与 React 教程
React TypeScript 学生应用
由 Mux 主办的 DEV 全球展示挑战赛:展示你的项目!

介绍
TypeScript 已发展成为 Web 开发领域最流行、应用最广泛的编程语言之一。TypeScript 是 JavaScript 的超集。简单来说,TypeScript 本质上是 JavaScript,但添加了一些额外的特性。TypeScript 的目标是编写强类型 JavaScript 代码。强类型有助于 Web 应用程序扩展,同时最大限度地减少 bug。TypeScript 代码通过 TypeScript Compiler 或 Babel 等编译器转换为 JavaScript。
为了更好地理解,我们将使用 React 和 TypeScript 开发一个学生应用程序。本教程将为你提供在下一个项目中使用 TypeScript 所需的一切。
如果您更喜欢在 YouTube 上观看此教程,请点击以下链接。
最终解决方案已在 GitHub 上提供。
先决条件
本教程假设您已具备React的基础知识。
为什么要使用 TypeScript?
使用 TypeScript 有很多好处。主要好处如下:
- 强类型确保在开发阶段就能发现错误,而不是在应用程序上线运行后才发现。此外,它也使代码调试变得更加容易。
- 文档 - 它为 JavaScript 代码提供文档,使其易于阅读和维护。
- 节省开发时间。
- TypeScript 中的泛型提供了一个强大的类型系统,为开发者提供了很大的灵活性。
TypeScript 学生应用程序
我们将使用 React 构建一个应用程序,该应用程序将涵盖 TypeScript 的以下几个方面。
- 道具
- 类型推断与类型注解
- 联合类型
- 组织界面
- 枚举
- 仿制药
应用开发:分步指南
要启动一个新的 TypeScript 应用,请使用以下命令
yarn create-react-app student-app --template typescript
npx create-react-app student-app --template typescript
进入 student-app 目录,或者yarn start如果npm start使用 npm。
道具
我们将首先向<App/>组件传递一个 prop。这是一个字符串,其中包含应用程序的名称。这里我们将看到 TypeScript 的第一个用例。
修改 `<app-name>`App.tsx和index.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;
在浏览器中,您应该可以看到学生应用。可以看到,AppName是一个接口,它有一个名为 name 的属性。
接口是一种抽象类型,编译器使用它来确定给定对象可以拥有哪些属性名称。它用于类型检查。
在AppName接口中,name 属性是一个字符串,这就是为什么我们将字符串传递给App组件的原因。如果您尝试传递任何其他类型,例如整数或布尔值,则应用会报错,甚至无法编译。如果您未使用 TypeScript,则应用可以编译,但在部署后会出现意外结果。
类型推断与类型注解
const [studentId,setStudentId] = useState(0)
setStudentId('test')
如果你尝试运行上面的代码,它将无法编译,TypeScript 会报错。这是因为 TypeScript 已经推断出 `x` 的类型studentId为整数(数字)。因此,如果你尝试将字符串赋值给 `x`,studentId就会在编译时产生错误。这在 TypeScript 中被称为类型推断。
同样,下面的代码在 TypeScript 中也无法运行。
const [student, setStudent] = useState(null)
setStudent({
name: 'Antony',
surname: 'Ndungu',
age: 15
})
这是因为 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;
从上面的代码可以看出,学生可以是对象,也可以是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
}
枚举
枚举类型用于存储常量值。在我们的示例中,学生级别可以是“本科生”或“研究生”。
export enum Level {
Undergraduate = "Undergraduate",
Postgraduate = "Postgraduate"
}
上述枚举类型可用于根据学生的年级有条件地显示学生的年龄,如下所示:
{
student?.level === Level.Undergraduate &&
<p><b>Age: {student.age}</b></p>
}
仿制药
泛型是 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>
</>
)
}
接口Item具有一个属性id,这意味着任何使用此组件的对象都必须具有该id属性。接口DisplayDataItem<T>是一个表示Array<T>数组类型的对象T,这意味着它可以被任何包含项目数组的对象使用。DisplayData是一个接受项目数组并显示列表的函数。
以下是App.tsx、App.css和data.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;
}
通用搜索功能
最后,我们将添加一个通用搜索功能,点击按钮即可根据学生姓名或年龄对学生列表进行排序。
创建一个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
}
//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;
结论
太棒了!至此,您已经掌握了使用 TypeScript 构建 React 应用的核心基础知识。在项目的最后一部分,我们介绍了 TypeScript 的一些高级特性,例如泛型。 祝您
使用 TypeScript 编码愉快!
如果您需要任何帮助,请随时在下方留言。
文章来源:https://dev.to/tndungu/typescript-with-react-tutorial-3ah5