TypeScript 是 JavaScript 的超集,通过静态类型检查在编译期捕获错误,提升代码质量和开发体验。
一、基础类型
1.1 变量声明
格式:
let/const 标识符: 数据类型 = 值
// 显式指定类型
let count: number = 666
let name: string = "hello ts"
// 类型推导(推荐,更简洁)
let age = 18 // 自动推导为 number
let msg = "hi" // 自动推导为 string
// age = "ok"; // ❌ 不能将 string 赋值给 number1.2 number 类型
let num: number = 100 // 十进制
num = 0b110 // 二进制
num = 0o567 // 八进制
num = 0xf123 // 十六进制1.3 boolean 类型
let isDone: boolean = true
let flag = 1 > 2 // 自动推导为 boolean1.4 string 类型
let userName: string = "malu"
let userAge: number = 18
let info = `my name is ${userName}, age is ${userAge}`1.5 Array 类型
// 两种写法
let names: string[] = ["a", "b", "c"]
let nums: Array<number> = [1, 2, 3]
// 数组中只能放指定类型
names.push("d")
// names.push(666); // ❌ number 不能赋值给 string1.6 Object 类型
// ❌ 不推荐:使用 object 类型无法访问具体属性
let obj1: object = { a: 1 }
// console.log(obj1.a); // 报错
// ✅ 推荐:明确属性类型
let obj2: { x: number; y: number } = { x: 1, y: 2 }
// ✅ 使用类型别名
type Point = { x: number; y: number }
let obj3: Point = { x: 1, y: 2 }1.7 Symbol 类型
// Symbol 创建唯一值,可用于对象的唯一键
let s1: symbol = Symbol("id")
let s2: symbol = Symbol("id")
const person = {
[s1]: "coding",
[s2]: "teacher",
}
// s1 !== s2,两个属性互不冲突1.8 null 和 undefined
let x: null = null
let y: undefined = undefined二、函数类型
2.1 参数类型与返回值
// 参数和返回值都指定类型
function sum(x: number, y: number): number {
return x + y
}
// 返回值可自动推导
let res = sum(1, 2) // res 推导为 number2.2 匿名函数参数
匿名函数的参数类型会根据上下文自动推导,无需手动标注
let names: string[] = ["malu", "wc", "xq"]
// ✅ 推荐:依赖自动推导
names.forEach((item) => console.log(item))
// 不推荐:手动标注(冗余)
names.forEach((item: string) => console.log(item))2.3 可选参数与默认值
// 可选参数(? 标记)
function greet(name: string, greeting?: string) {
if (greeting) {
console.log(`${greeting}, ${name}!`)
}
}
// 默认值参数(无需指定类型,自动推导)
function add(a: number, b: number, c = 0) {
return a + b + c
}2.4 剩余参数
function sum(first: number, ...rest: number[]) {
return rest.reduce((acc, cur) => acc + cur, first)
}
sum(1, 2, 3, 4) // 102.5 函数类型表达式
// 定义函数类型
type MathFn = (a: number, b: number) => number
const add: MathFn = (a, b) => a + b
const mul: MathFn = (a, b) => a * b
function calc(fn: MathFn, x: number, y: number) {
return fn(x, y)
}2.6 函数重载
// 重载签名
function concat(a: number, b: number): number
function concat(a: string, b: string): string
// 实现签名
function concat(a: any, b: any): any {
return a + b
}
concat(1, 2) // 3
concat("hi", " ts") // "hi ts"
// concat(1, "hi"); // ❌ 没有匹配的重载2.7 调用签名与构造签名
// 调用签名:描述一个可调用且有属性的对象
interface CallableWithProps {
(num: number): number
description: string
}
// 构造签名:描述可以 new 的类型
interface Constructor {
new (): object
}
function factory(cls: Constructor) {
return new cls()
}三、类型系统
3.1 联合类型
// 使用 | 组合多种类型
type StringOrNumber = number | string
function format(value: StringOrNumber) {
if (typeof value === "string") {
return value.toUpperCase() // 类型缩小后可调用 string 方法
}
return value.toFixed(2)
}3.2 字面量类型
// 字面量本身就是类型
type Direction = "left" | "right" | "up" | "down"
type HttpMethod = "GET" | "POST" | "PUT" | "DELETE"
function move(dir: Direction) {
/* ... */
}
move("left") // ✅
// move("top"); // ❌3.3 类型别名(type)
type ID = number | string
type Point = {
x: number
y: number
}
type Callback = (data: string) => void3.4 接口(interface)
interface IPerson {
name: string
age: number
score?: number // 可选属性
}
// 接口可以重复声明(自动合并)
interface IPerson {
email?: string
}
// 接口可以继承
interface IStudent extends IPerson {
grade: number
}3.5 interface vs type
| 特性 | interface | type |
|---|---|---|
| 约束对象类型 | ✅ | ✅ |
| 约束基本类型/联合类型 | ❌ | ✅ |
| 重复声明合并 | ✅ | ❌ |
| 继承(extends) | ✅ | ❌(可用 & 交叉) |
| 被类实现(implements) | ✅ | ✅ |
使用建议:约束对象结构用
interface,其他场景用type
3.6 交叉类型
interface ITeacher {
name: string
age: number
}
interface ICoder {
name: string
coding: () => void
}
// 交叉类型:必须同时满足所有条件
type FullStack = ITeacher & ICoder
let person: FullStack = {
name: "ml",
age: 18,
coding() {
console.log("coding...")
},
}3.7 类型缩小
通过条件判断将宽泛类型收窄为具体类型
// typeof
function format(value: number | string) {
if (typeof value === "string") {
return value.length // string 方法
}
return value.toFixed(2) // number 方法
}
// instanceof
function print(date: string | Date) {
if (date instanceof Date) {
console.log(date.getFullYear())
}
}
// in 操作符
interface ISwim {
swim: () => void
}
interface IRun {
run: () => void
}
function move(animal: ISwim | IRun) {
if ("swim" in animal) {
animal.swim()
} else {
animal.run()
}
}
// === 严格等于
type Direction = "left" | "right" | "up" | "down"
function handleDir(dir: Direction) {
if (dir === "left") {
console.log("向左移动")
}
}3.8 类型断言
// as 语法
const el = document.getElementById("logo") as HTMLImageElement
el.src = "xxx"
// 非空断言(!):确信值不为 null/undefined 时使用
interface IPerson {
friend?: { name: string }
}
const info: IPerson = { friend: { name: "xq" } }
console.log(info.friend!.name) // 确信 friend 存在
// ⚠️ 非空断言有风险,优先使用可选链 ?.
console.log(info.friend?.name)四、特殊类型
4.1 any
任意类型,等于回退到 JavaScript,尽量避免使用
let a: any = 1
a = "hi"
a = true
let names: any[] = [1, true, "hi", undefined]4.2 unknown
比 any 更安全的未知类型,使用前必须进行类型缩小
let value: unknown
value = 123
value = "hello"
// value.length; // ❌ 不能直接使用
// ✅ 类型缩小后使用
if (typeof value === "string") {
console.log(value.length)
}4.3 void
表示函数没有返回值
function log(msg: string): void {
console.log(msg)
}
// 函数类型的 void 返回值允许实际返回值(上下文推导场景)
type VoidFn = () => void
const fn: VoidFn = () => 123 // 不报错,但返回值类型仍是 void4.4 never
表示永远不会有返回值(死循环、抛异常)
function throwError(msg: string): never {
throw new Error(msg)
}
function infiniteLoop(): never {
while (true) {}
}4.5 tuple(元组)
固定长度和类型的数组
// 每个位置的类型都确定
let info: [string, number, boolean] = ["ml", 18, true]
// 实际应用:模拟 React useState
function useState<T>(initial: T): [T, (newValue: T) => void] {
let state = initial
function setState(newValue: T) {
state = newValue
}
return [state, setState]
}
const [count, setCount] = useState(10)4.6 枚举(enum)
enum Direction {
LEFT, // 0
RIGHT, // 1
UP, // 2
DOWN, // 3
}
// 指定起始值
enum StatusCode {
OK = 200,
NOT_FOUND = 404,
SERVER_ERROR = 500,
}
let dir: Direction = Direction.LEFT // 0五、面向对象(OOP)
5.1 类的定义
class Animal {
name: string
age: number
constructor(name: string, age: number) {
this.name = name
this.age = age
}
run() {
console.log(`${this.name} run...`)
}
}
let a = new Animal("旺财", 3)
a.run() // "旺财 run..."5.2 类的继承
class Cat extends Animal {
weight: number
constructor(name: string, age: number, weight: number) {
super(name, age) // 调用父类构造函数
this.weight = weight
}
meow() {
console.log(`${this.name} 喵喵~`)
}
}
let cat = new Cat("小黑", 2, 5)
cat.run() // 继承自 Animal
cat.meow() // 自己的方法5.3 成员修饰符
| 修饰符 | 访问范围 | 说明 |
|---|---|---|
public | 任何位置 | 默认值 |
private | 仅类内部 | 类外和子类都无法访问 |
protected | 类内部 + 子类 | 类外无法访问 |
readonly | 只读 | 初始化后不可修改 |
class Person {
public name: string
private _age: number
protected score: number
readonly id: number
constructor(name: string, age: number, score: number, id: number) {
this.name = name
this._age = age
this.score = score
this.id = id
}
}
let p = new Person("ml", 18, 99, 1)
p.name // ✅ public
// p._age; // ❌ private
// p.score; // ❌ protected
// p.id = 2; // ❌ readonly5.4 参数属性(简写)
// 等价于声明属性 + 构造函数赋值
class User {
constructor(
public name: string,
public age: number,
private _email: string,
) {}
}
let u = new User("ml", 18, "[email protected]")
console.log(u.name) // "ml"5.5 getter / setter
class Person {
private _age: number
constructor(
public name: string,
age: number,
) {
this._age = age
}
get age() {
return this._age
}
set age(value: number) {
if (value >= 0 && value < 150) {
this._age = value
}
}
}
let p = new Person("ml", 18)
p.age = 200 // setter 拦截,不生效
console.log(p.age) // 185.6 抽象类
抽象类不能被实例化,只能被继承。抽象方法必须在子类中实现。
abstract class Shape {
abstract getArea(): number
}
class Rectangle extends Shape {
constructor(
public width: number,
public height: number,
) {
super()
}
getArea() {
return this.width * this.height
}
}
class Circle extends Shape {
constructor(public radius: number) {
super()
}
getArea() {
return Math.PI * this.radius ** 2
}
}
// 多态:父类引用指向子类对象
function printArea(shape: Shape) {
console.log(shape.getArea())
}
printArea(new Rectangle(10, 5)) // 50
printArea(new Circle(5)) // 78.545.7 接口实现(implements)
interface ISwimmable {
swim: () => void
}
interface IRunnable {
run: () => void
}
// 一个类可以实现多个接口
class Duck implements ISwimmable, IRunnable {
constructor(public name: string) {}
swim() {
console.log(`${this.name} 在游泳`)
}
run() {
console.log(`${this.name} 在跑`)
}
}六、泛型
6.1 基本使用
泛型让类型参数化,在调用时才确定具体类型
function identity<T>(arg: T): T {
return arg
}
// 显式指定类型
let r1 = identity<number>(123)
let r2 = identity<string>("hi ts")
// 自动推导(更简洁)
let r3 = identity(123) // T = number
let r4 = identity({ x: 1 }) // T = { x: number }6.2 多个泛型参数
function pair<T, U>(first: T, second: U): [T, U] {
return [first, second]
}
pair<number, string>(1, "hello")
pair(1, "hello") // 自动推导6.3 泛型接口
interface IResponse<T> {
code: number
message: string
data: T
}
// 使用时指定 T 的具体类型
type UserResponse = IResponse<{ name: string; age: number }>
type ListResponse = IResponse<string[]>6.4 泛型类
class Container<T> {
constructor(public value: T) {}
getValue(): T {
return this.value
}
}
let numBox = new Container<number>(100)
let strBox = new Container("hello") // T 自动推导为 string6.5 泛型约束
// 约束 T 必须有 length 属性
interface IHasLength {
length: number
}
function getLength<T extends IHasLength>(arg: T): number {
return arg.length
}
getLength("hello") // ✅ string 有 length
getLength([1, 2, 3]) // ✅ 数组有 length
getLength({ length: 10 }) // ✅ 对象有 length
// getLength(123); // ❌ number 没有 length6.6 keyof 与泛型约束
// keyof 获取对象所有键的联合类型
interface IPerson {
name: string
age: number
}
type PersonKeys = keyof IPerson // "name" | "age"
// 约束 key 必须是 obj 的属性名
function getProperty<O, K extends keyof O>(obj: O, key: K) {
return obj[key]
}
const info = { name: "ml", age: 18, height: 1.88 }
getProperty(info, "name") // ✅
// getProperty(info, "score"); // ❌ "score" 不是 info 的属性七、类型声明文件
7.1 两种文件类型
| 文件类型 | 说明 |
|---|---|
.ts | 包含业务逻辑和类型,会编译为 .js |
.d.ts | 仅包含类型声明,不生成 .js,用于提供类型信息 |
7.2 三种声明文件来源
| 来源 | 说明 | 示例 |
|---|---|---|
| 内置 | TS 内置的标准 API 声明 | Date, Math, Promise 等 |
| 第三方 | npm 包自带或 @types/* 包 | axios(自带)、@types/react |
| 自定义 | 项目中手动编写的 .d.ts | 为 JS 文件提供类型声明 |
7.3 第三方库的类型声明
# 有些库自带类型声明(如 axios)
npm install axios
# 有些需要额外安装(如 lodash)
npm install lodash
npm install @types/lodash -D7.4 自定义类型声明
// utils.d.ts —— 为 utils.js 提供类型声明
declare function formatDate(date: Date): string
declare const VERSION: string
// 声明模块
declare module "*.vue" {
import type { DefineComponent } from "vue"
const component: DefineComponent
export default component
}八、this 类型处理
8.1 函数中的 this
// 在第一个参数位置指定 this 的类型(不占用实际参数位)
function greet(this: { name: string }, msg: string) {
console.log(`${this.name}: ${msg}`)
}
greet.call({ name: "ml" }, "hello")8.2 内置 this 工具类型
| 工具类型 | 说明 |
|---|---|
ThisParameterType<T> | 提取函数类型中 this 的类型 |
OmitThisParameter<T> | 移除函数类型中的 this 参数 |
ThisType<T> | 指定对象中方法的 this 类型 |
function fn(this: { name: string }, info: string) {
console.log(this.name, info)
}
type FnThis = ThisParameterType<typeof fn> // { name: string }
type PureFn = OmitThisParameter<typeof fn> // (info: string) => void九、Vue3 中使用 TypeScript
9.1 ref 与类型
import { ref } from "vue"
// 简单类型自动推导
const count = ref(0) // Ref<number>
// 复杂类型需显式指定
interface TodoItem {
id: number
name: string
done: boolean
}
const list = ref<TodoItem[]>([])9.2 reactive 与类型
import { reactive } from "vue"
interface Book {
title: string
year?: number
}
const book: Book = reactive({
title: "学习 Vue3 + TS",
})
book.year = 20249.3 computed 与类型
import { ref, computed } from "vue"
const count = ref(200)
// 自动推导(推荐)
const double = computed(() => count.value * 2)
// 显式指定
const formatted = computed<string>(() => (count.value * 2).toFixed(2))9.4 defineProps 与类型
<script setup lang="ts">
// 基础写法
defineProps<{
money: number
car?: string
}>()
// 带默认值
withDefaults(
defineProps<{
money: number
car?: string
}>(),
{
money: 100,
car: "bicycle",
},
)
</script>9.5 defineEmits 与类型
<script setup lang="ts">
const emit = defineEmits<{
(e: "changeMoney", money: number): void
(e: "changeCar", car: string): void
}>()
// 触发事件
emit("changeMoney", 800)
</script>9.6 事件处理与类型
<script setup lang="ts">
const handleChange = (e: Event) => {
const target = e.target as HTMLInputElement
console.log(target.value)
}
</script>
<template>
<input type="text" @change="handleChange" />
</template>9.7 常见工具类型
| 工具类型 | 说明 | 示例 |
|---|---|---|
Partial<T> | 所有属性变为可选 | Partial<User> |
Required<T> | 所有属性变为必选 | Required<User> |
Readonly<T> | 所有属性变为只读 | Readonly<User> |
Pick<T, K> | 选取部分属性 | Pick<User, "name" | "age"> |
Omit<T, K> | 排除部分属性 | Omit<User, "password"> |
Record<K, T> | 构造键值对类型 | Record<string, number> |
Exclude<T, U> | 从联合类型中排除 | Exclude<"a" | "b", "a"> → "b" |
Extract<T, U> | 从联合类型中提取 | Extract<"a" | "b", "a"> → "a" |