前置要求:JavaScript基础
学习时长:约2-3天(每天3小时)
TypeScript版本:5.x
适用场景:大型项目、团队协作、企业级应用
一、TypeScript 是什么?
TypeScript 是 JavaScript 的超集,添加了类型系统,可以在编译时发现错误,提高代码质量和开发效率。
| 特点 | 说明 |
|---|---|
| 类型安全 | 编译时检查类型错误 |
| 智能提示 | IDE自动补全更准确 |
| 代码可读性 | 类型即文档 |
| 兼容性 | 完全兼容JavaScript |
| 学习成本 | 有JS基础1-2天可入门 |
二、安装与配置
# 安装TypeScript
npm install -g typescript
# 初始化配置
tsc --init
# 编译
tsc index.ts
# 使用ts-node直接运行
npm install -g ts-node
ts-node index.ts
# 项目中使用(Vite)
npm create vite@latest my-app -- --template vue-ts
三、基础类型
// ====================
// 基本类型
// ====================
let name: string = '张三'
let age: number = 25
let isActive: boolean = true
let nothing: null = null
let undef: undefined = undefined
// 数组
let numbers: number[] = [1, 2, 3]
let names: Array<string> = ['张三', '李四']
// 元组
let tuple: [string, number] = ['张三', 25]
// 枚举
enum Status {
Pending = 'pending',
Active = 'active',
Disabled = 'disabled'
}
let status: Status = Status.Active
// any(任意类型,不推荐)
let data: any = 'hello'
// unknown(安全的any)
let input: unknown = 'hello'
if (typeof input === 'string') {
console.log(input.toUpperCase()) // 需要类型检查
}
// void(无返回值)
function greet(): void {
console.log('hello')
}
// never(永不返回)
function throwError(msg: string): never {
throw new Error(msg)
}
四、接口与类型别名
// ====================
// 接口(Interface)
// ====================
interface User {
id: number
username: string
email: string
age?: number // 可选属性
readonly createdAt: Date // 只读属性
}
const user: User = {
id: 1,
username: '张三',
email: 'zhangsan@example.com',
createdAt: new Date()
}
// 接口继承
interface Admin extends User {
role: 'admin' | 'superadmin'
permissions: string[]
}
// ====================
// 类型别名(Type)
// ====================
type ID = number | string
type UserRole = 'admin' | 'editor' | 'user'
type ApiResponse<T> = {
code: number
message: string
data: T
}
// 使用
const userId: ID = 123
const role: UserRole = 'admin'
const response: ApiResponse<User> = {
code: 200,
message: 'success',
data: user
}
// ====================
// 接口 vs 类型别名
// ====================
// 接口:可以extends和implements,可以声明合并
// 类型别名:可以定义联合类型、交叉类型、元组
// 推荐:对象用interface,其他用type
五、函数类型
// ====================
// 函数参数和返回值
// ====================
function add(a: number, b: number): number {
return a + b
}
// 可选参数
function greet(name: string, greeting?: string): string {
return `${greeting || 'Hello'}, ${name}!`
}
// 默认参数
function createUser(name: string, role: UserRole = 'user'): User {
return { id: 1, username: name, email: '', createdAt: new Date() }
}
// 剩余参数
function sum(...numbers: number[]): number {
return numbers.reduce((a, b) => a + b, 0)
}
// ====================
// 函数类型
// ====================
type MathFunc = (a: number, b: number) => number
const multiply: MathFunc = (a, b) => a * b
const divide: MathFunc = (a, b) => a / b
// ====================
// 函数重载
// ====================
function format(value: string): string
function format(value: number): string
function format(value: Date): string
function format(value: string | number | Date): string {
if (typeof value === 'string') return value
if (typeof value === 'number') return value.toFixed(2)
return value.toISOString()
}
六、泛型
// ====================
// 泛型函数
// ====================
function identity<T>(arg: T): T {
return arg
}
const num = identity<number>(123) // number
const str = identity<string>('hello') // string
const auto = identity('hello') // 自动推断为string
// ====================
// 泛型接口
// ====================
interface ApiResponse<T> {
code: number
message: string
data: T
}
interface PaginatedData<T> {
items: T[]
total: number
page: number
size: number
}
// 使用
const usersResponse: ApiResponse<PaginatedData<User>> = {
code: 200,
message: 'success',
data: {
items: [],
total: 0,
page: 1,
size: 10
}
}
// ====================
// 泛型约束
// ====================
interface HasId {
id: number
}
function findById<T extends HasId>(items: T[], id: number): T | undefined {
return items.find(item => item.id === id)
}
// ====================
// 泛型工具类型
// ====================
interface User {
id: number
username: string
email: string
password: string
}
// Partial:所有属性变为可选
type PartialUser = Partial<User>
// Required:所有属性变为必需
type RequiredUser = Required<PartialUser>
// Pick:选取部分属性
type UserBasic = Pick<User, 'id' | 'username'>
// Omit:排除部分属性
type UserWithoutPassword = Omit<User, 'password'>
// Record:构造键值对类型
type UserMap = Record<string, User>
// Exclude:从联合类型中排除
type StringOrNumber = string | number | boolean
type NotBoolean = Exclude<StringOrNumber, boolean> // string | number
// ReturnType:获取函数返回类型
type GetUser = () => User
type GetUserReturn = ReturnType<GetUser> // User
七、类
// ====================
// 类定义
// ====================
class User {
// 属性声明
public name: string
private password: string
protected email: string
readonly id: number
// 静态属性
static count: number = 0
// 构造函数
constructor(id: number, name: string, email: string, password: string) {
this.id = id
this.name = name
this.email = email
this.password = password
User.count++
}
// 方法
public greet(): string {
return `我是${this.name}`
}
// getter
get displayName(): string {
return this.name
}
// setter
set displayName(value: string) {
this.name = value
}
// 静态方法
static getCount(): number {
return User.count
}
}
// ====================
// 继承
// ====================
class Admin extends User {
role: string
constructor(id: number, name: string, email: string, password: string, role: string) {
super(id, name, email, password)
this.role = role
}
greet(): string {
return `${super.greet()}(${this.role})`
}
}
// ====================
// 抽象类
// ====================
abstract class Shape {
abstract area(): number
abstract perimeter(): number
describe(): string {
return `面积: ${this.area().toFixed(2)}, 周长: ${this.perimeter().toFixed(2)}`
}
}
class Circle extends Shape {
constructor(private radius: number) {
super()
}
area(): number {
return Math.PI * this.radius ** 2
}
perimeter(): number {
return 2 * Math.PI * this.radius
}
}
八、React + TypeScript
// ====================
// 组件Props类型
// ====================
interface UserCardProps {
user: User
onEdit: (id: number) => void
onDelete: (id: number) => void
children?: React.ReactNode
}
function UserCard({ user, onEdit, onDelete, children }: UserCardProps) {
return (
<div>
<h3>{user.username}</h3>
<p>{user.email}</p>
<button onClick={() => onEdit(user.id)}>编辑</button>
<button onClick={() => onDelete(user.id)}>删除</button>
{children}
</div>
)
}
// ====================
// useState 类型
// ====================
const [user, setUser] = useState<User | null>(null)
const [users, setUsers] = useState<User[]>([])
const [loading, setLoading] = useState<boolean>(false)
const [error, setError] = useState<string | null>(null)
// ====================
// useRef 类型
// ====================
const inputRef = useRef<HTMLInputElement>(null)
const canvasRef = useRef<HTMLCanvasElement>(null)
// ====================
// 事件处理
// ====================
const handleClick = (e: React.MouseEvent<HTMLButtonElement>) => {
console.log(e.currentTarget.value)
}
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setValue(e.target.value)
}
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault()
}
// ====================
// 自定义Hook类型
// ====================
interface UseFetchResult<T> {
data: T | null
loading: boolean
error: string | null
refetch: () => void
}
function useFetch<T>(url: string): UseFetchResult<T> {
const [data, setData] = useState<T | null>(null)
const [loading, setLoading] = useState(true)
const [error, setError] = useState<string | null>(null)
const refetch = useCallback(async () => {
setLoading(true)
setError(null)
try {
const response = await fetch(url)
const result = await response.json()
setData(result)
} catch (err) {
setError(err instanceof Error ? err.message : '请求失败')
} finally {
setLoading(false)
}
}, [url])
useEffect(() => { refetch() }, [refetch])
return { data, loading, error, refetch }
}
九、Vue 3 + TypeScript
<script setup lang="ts">
import { ref, computed } from 'vue'
// 类型定义
interface User {
id: number
username: string
email: string
}
// ref类型
const user = ref<User | null>(null)
const users = ref<User[]>([])
const loading = ref(false)
// computed类型
const userCount = computed<number>(() => users.value.length)
// Props类型
interface Props {
title: string
users: User[]
loading?: boolean
}
const props = withDefaults(defineProps<Props>(), {
loading: false
})
// Emits类型
const emit = defineEmits<{
(e: 'update', id: number): void
(e: 'delete', id: number): void
}>()
// 函数类型
const fetchUser = async (id: number): Promise<User> => {
const response = await fetch(`/api/users/${id}`)
return response.json()
}
</script>
十、常见模式
// ====================
// API请求封装
// ====================
class ApiClient {
constructor(private baseUrl: string) {}
async get<T>(path: string): Promise<ApiResponse<T>> {
const response = await fetch(`${this.baseUrl}${path}`)
return response.json()
}
async post<T>(path: string, data: unknown): Promise<ApiResponse<T>> {
const response = await fetch(`${this.baseUrl}${path}`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data)
})
return response.json()
}
}
const api = new ApiClient('/api')
const { data: users } = await api.get<User[]>('/users')
// ====================
// 类型守卫
// ====================
function isUser(obj: unknown): obj is User {
return typeof obj === 'object' && obj !== null && 'username' in obj
}
// ====================
// 映射类型
// ====================
type Readonly<T> = {
readonly [P in keyof T]: T[P]
}
type Optional<T> = {
[P in keyof T]?: T[P]
}
学习建议
- 先掌握基础类型,理解string/number/boolean/array/object
- 学习接口和类型别名,这是定义数据结构的核心
- 理解泛型,提高代码复用性
- 在实际项目中使用,Vue/React + TypeScript是主流
- 不要过度类型化,简单的地方让TS自动推断