在前端开发的时候,JavaScript作为弱类型语言还是太不抗压了,不能随便写的情况下它能让你随便写就离谱。今天我们谈谈其超集——TypeScript,它不仅为 JavaScript 增加了类型系统,还提供了很多开发工具支持。
为什么需要 TypeScript?
JavaScript 作为一门动态类型语言,虽然灵活,但在大型项目中容易出现类型相关的错误。TypeScript 作为 JavaScript 的超集,添加了静态类型检查,能够在编译阶段发现潜在问题,提高代码质量和开发效率。
// App.tsx
import { useState } from 'react'
import './App.css'
import HelloComponent from './components/HelloComponent.tsx'
function App() {
// 基本类型声明
let count: number = 10;
const title: string = 'hello ts'
const isDone: boolean = true
const list: number[] = [1, 2, 3]
// 元组类型
const tuple: [number, string] = [1, '2']
// 枚举类型
enum Status {
Pending,
Fullfilled,
Rejected
}
const pStatus: Status = Status.Pending
// 对象类型约束 - 使用接口
interface User {
name: string;
age: number;
isSingle?: boolean;
}
const user: User = {
name: 'zhangsan',
age: 18,
isSingle: true
}
return (
<>
{count}
{title}
{Status.Fullfilled}
{user.name}
<HelloComponent name='zhangsan' age={18} />
</>
)
}
export default App
通过类型声明,我们可以在编码阶段,就发现类型错误,js就要编译阶段了,也就是代码运行之后,比如将字符串赋值给 number 类型的变量等。大红大红的还挺好看的。
React 组件中的 TypeScript
在 React 也是疯狂支持TypeScript:
// HelloComponent.tsx
import React from 'react'
interface HelloComponentProps {
name: string;
age?: number; // 可选属性
}
const HelloComponent: React.FC<HelloComponentProps> = (props) => {
return (
<h2>hello user: {props.name}, age: {props.age}</h2>
)
}
export default HelloComponent
使用 React.FC<Props>
泛型来约束函数组件,确保组件接收正确的 props 类型。
泛型?之前了解的时候印象里面好像还有任意类型的泛型 是的,TypeScript 中有任意类型的泛型。以下是几种常见的方式:
- 使用
any
类型:
interface HelloComponentProps {
name: string;
age?: number;
[key: string]: any; // 允许任意额外属性
}
const HelloComponent: React.FC<HelloComponentProps> = (props) => {
return (
<h2>hello user:{props.name}, age:{props.age}</h2>
)
}
- 使用
unknown
类型(更安全):
interface HelloComponentProps {
name: string;
age?: number;
extraData?: unknown; // 任意类型但需要类型检查
}
- 使用泛型参数:
interface HelloComponentProps<T = any> {
name: string;
age?: number;
extraData?: T; // 可以是任意类型
}
const HelloComponent: React.FC<HelloComponentProps> = (props) => {
return (
<h2>hello user:{props.name}, age:{props.age}</h2>
)
}
- 使用联合类型:
interface HelloComponentProps {
name: string;
age?: number;
value?: string | number | boolean | object; // 支持多种特定类型
}
其中 any
类型是最宽松的,允许任何类型的值,但会失去类型检查的优势。在实际开发中,建议优先使用更具体的类型或 unknown
来保证类型安全。
为什么unkonwn更安全,简单来说:就是说我虽然能用unknown传入任意值,但是我不能乱用。比如传入是number但不能作为string来用。any就没这方面的烦恼了。
状态管理和事件处理
在状态管理方面,TypeScript 同样提供了强大的支持。特别是在处理表单和事件时,类型约束能够有效避免运行时错误:
// NameEditComponent.tsx
import React from 'react'
interface Props {
userName: string;
onChange?: (e: React.ChangeEvent<HTMLInputElement>) => void;
}
const NameEditComponent: React.FC<Props> = (props) => {
return (
<>
<label>Update name</label>
<input value={props.userName} onChange={props.onChange} />
</>
)
}
export default NameEditComponent
通过约束 onChange
事件处理器的参数类型,我们确保了事件对象的正确使用。
除了<HTMLInputElement>
还有一些其他常见的 HTML 元素类型例子:
// 1. 文本域 (textarea) 元素
const handleTextAreaChange = (event: React.ChangeEvent<HTMLTextAreaElement>) => {
console.log(event.target.value);
};
<textarea onChange={handleTextAreaChange} />
// 2. 选择框 (select) 元素
const handleSelectChange = (event: React.ChangeEvent<HTMLSelectElement>) => {
console.log(event.target.value);
};
<select onChange={handleSelectChange}>
<option value="1">选项1</option>
<option value="2">选项2</option>
</select>
// 3. 按钮 (button) 元素
const handleButtonClick = (event: React.MouseEvent<HTMLButtonElement>) => {
console.log('按钮被点击');
};
<button onClick={handleButtonClick}>点击我</button>
// 4. 输入框 (input) 元素 - 特定于不同类型的输入
const handleCheckboxChange = (event: React.ChangeEvent<HTMLInputElement>) => {
console.log(event.target.checked); // checkbox 使用 checked 而不是 value
};
<input
type="checkbox"
onChange={handleCheckboxChange}
/>
// 5. 表单 (form) 元素
const handleFormSubmit = (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault(); // 阻止默认提交行为
console.log('表单提交');
};
<form onSubmit={handleFormSubmit}>
{/* 表单内容 */}
</form>
// 6. div 或其他 HTML 元素
const handleClickDiv = (event: React.MouseEvent<HTMLDivElement>) => {
console.log('div 被点击');
};
<div onClick={handleClickDiv}>点击这个 div</div>
重点说明:
ChangeEvent
通常用于表单元素的值变化事件(如 input, textarea, select)MouseEvent
用于鼠标相关事件(如 click, mouseover 等)FormEvent
专门用于表单事件(如 submit)- 每种 HTML 元素都有对应的类型定义,如
HTMLInputElement
、HTMLTextAreaElement
等 - 这些类型帮助 TypeScript 理解事件对象中 target 的具体属性和方法
当然,你如果嫌烦,也可以直接不写(大红但不影响运行)或者any(直接就是回到js)
在 App 中使用组件
将这些组件整合到主应用中,我们可以看到完整的类型安全流程:
// App.tsx
import { useState } from 'react'
import './App.css'
import NameEditComponent from './components/NameEditComponent'
function App() {
// 使用 TypeScript 约束 state 类型
const [name, setName] = useState<string>('initialName')
// 约束事件处理函数参数类型
const setUsernameState = (event: React.ChangeEvent<HTMLInputElement>) => {
setName(event.target.value)
}
return (
<>
<NameEditComponent userName={name} onChange={setUsernameState} />
</>
)
}
export default App
useState<string>
明确指定了状态的类型,使得后续对状态的操作都有类型安全保障。
小结
虽然 TypeScript 在初期需要写更多的类型声明代码,但这些投入会在项目规模增大时带来显著回报。它不仅能减少 bug,还能提高开发效率和代码质量。特别是在团队协作中,良好的类型约束能够大大降低沟通成本。
虽然即使 TypeScript 报错,只要 JavaScript 能运行,代码仍然可以执行。但还是建议在开发阶段解决所有类型错误。