案例一:基础类型实战(最常用,必掌握)
需求说明
定义一个"用户基础信息"变量,包含姓名、年龄、是否是学生,要求用 TS 约束类型,避免赋值错误;再写一个简单函数,打印用户信息,确保函数参数和返回值类型正确。
完整可运行代码
typescript
// 1. 定义基础类型变量(逐行注释,看不懂的看注释)
// 姓名:字符串类型(string),只能赋值字符串
let userName: string = "小明";
// 年龄:数字类型(number),只能赋值数字(整数、小数都可以)
let userAge: number = 18;
// 是否是学生:布尔类型(boolean),只能赋值 true / false
let isStudent: boolean = true;
// 2. 定义函数:打印用户信息
// 函数参数:name(字符串)、age(数字)、isStu(布尔,可选参数,用 ? 表示)
// 函数返回值:void(无返回值,即函数不return任何内容)
function printUserInfo(name: string, age: number, isStu?: boolean): void {
// 拼接用户信息字符串
let info: string = `姓名:${name},年龄:${age}`;
// 如果isStu存在(可选参数可能为undefined,所以要判断)
if (isStu) {
info += ",身份:学生";
} else {
info += ",身份:非学生";
}
// 打印结果
console.log(info);
}
// 3. 调用函数(测试是否能正常运行)
// 正确调用:参数类型和顺序和函数定义一致
printUserInfo(userName, userAge, isStudent);
// 错误调用(注释掉,否则会报错,演示TS的类型校验作用)
// printUserInfo(123, "18", "是"); // 报错:参数1必须是string,参数2必须是number,参数3必须是boolean
逐行解析(重点!零基础必看)
-
let userName: string = "小明";:: string是 TS 的类型注解,告诉 TS"这个变量只能存字符串",如果赋值123或true,TS 会直接报错(提前规避错误)。 -
let userAge: number = 18;:: number约束变量只能存数字,赋值"18"(字符串)会报错。 -
let isStudent: boolean = true;:: boolean约束变量只能存true或false。 -
function printUserInfo(name: string, age: number, isStu?: boolean): void {}:name: string:函数第一个参数必须是字符串;age: number:函数第二个参数必须是数字;isStu?: boolean:?表示"可选参数",可以传,也可以不传,传的话必须是布尔值;: void:函数没有返回值(如果函数有 return,这里要写返回值的类型,比如: string)。
-
if (isStu) {}:因为isStu是可选参数,可能为undefined,所以必须判断,否则会有潜在错误(TS 会提醒你)。 -
错误调用演示:注释掉的代码如果解开,TS 会直接报错,这就是 TS 的核心作用------提前检查类型错误,不用等到运行时才发现问题。
运行步骤(手把手教)
- 新建文件:在桌面新建文件夹
ts-demo,打开文件夹,新建文件demo1.ts; - 复制代码:将上面的完整代码复制到
demo1.ts中; - 编译 TS:打开终端(按住 shift+右键,选择"在此处打开终端"),输入
tsc demo1.ts,会生成demo1.js文件(TS 编译成 JS); - 运行 JS:输入
node demo1.js,终端会输出:姓名:小明,年龄:18,身份:学生; - 测试错误:解开注释的错误调用代码,再执行
tsc demo1.ts,会看到 TS 报错,提示"参数类型不匹配"(这就是 TS 的类型保护)。
常见问题(新手必踩坑)
- 报错"找不到名称 'userName'":检查变量是否定义,或者变量名是否拼写错误(TS 区分大小写);
- 运行
tsc提示"不是内部或外部命令":环境没搭好,看末尾附录的环境搭建步骤; - 可选参数传了非布尔值:比如
printUserInfo(userName, userAge, "是"),会报错,因为isStu必须是布尔值。
案例二:对象类型实战(项目高频,重点掌握)
需求说明
定义一个"用户详情"对象,包含 id、姓名、年龄、邮箱、是否VIP(可选),要求用 TS 约束对象的结构(不能多属性、不能少属性,属性类型不能错);再写一个函数,修改用户年龄,确保修改后的数据类型依然正确。
完整可运行代码
typescript
// 1. 用 interface 定义对象结构(核心!项目里最常用)
// interface 就是"接口",用来规定对象的"格式":有哪些属性、每个属性是什么类型
interface User {
id: number; // 必须有id,类型是数字
name: string; // 必须有name,类型是字符串
age: number; // 必须有age,类型是数字
email: string; // 必须有email,类型是字符串
isVip?: boolean; // 可选属性:可以有,也可以没有,类型是布尔
}
// 2. 定义符合 interface 格式的对象(严格按照上面的规则来)
const user: User = {
id: 1001,
name: "小红",
age: 20,
email: "xiaohong@163.com",
// isVip 可选,这里可以不写
};
// 3. 定义函数:修改用户年龄(确保修改后年龄还是数字)
// 函数参数:user(必须是 User 类型)、newAge(必须是数字)
// 函数返回值:User(返回修改后的用户对象,类型还是 User)
function updateUserAge(user: User, newAge: number): User {
// 修改年龄(注意:newAge 必须是数字,否则 TS 报错)
user.age = newAge;
// 返回修改后的用户对象
return user;
}
// 4. 调用函数,测试效果
const updatedUser = updateUserAge(user, 21);
// 打印修改后的用户信息
console.log("修改后的用户:", updatedUser);
// 错误演示(注释掉,否则报错)
// const errorUser: User = {
// id: "1002", // 报错:id 必须是数字,不能是字符串
// name: "小李",
// // 报错:缺少 age 属性(interface 里规定必须有 age)
// email: "xiaoli@163.com"
// };
逐行解析(重点!)
-
interface User { ... }:这是 TS 定义对象类型的核心方式,叫做"接口",作用是规定对象的结构:- 里面的每一行都是"属性名: 类型",比如
id: number表示"对象必须有 id 属性,且类型是数字"; isVip?: boolean:?表示可选属性,有没有都可以,不写不会报错;- 如果定义的对象不符合这个接口(比如少属性、属性类型错),TS 会直接报错。
- 里面的每一行都是"属性名: 类型",比如
-
const user: User = { ... }:: User表示"这个 user 对象必须符合 User 接口的格式",里面的属性不能多、不能少,类型不能错。 -
function updateUserAge(user: User, newAge: number): User { ... }:- 第一个参数
user: User:约束传入的参数必须是 User 类型的对象; - 第二个参数
newAge: number:约束传入的新年龄必须是数字; - 返回值
: User:约束函数返回的必须是 User 类型的对象,确保修改后的数据格式依然正确。
- 第一个参数
-
错误演示:注释掉的
errorUser有两个错误:① id 是字符串(应该是数字);② 缺少 age 属性(接口规定必须有),所以 TS 会报错,提前规避错误。
运行步骤
- 在
ts-demo文件夹中,新建文件demo2.ts; - 复制上面的完整代码到
demo2.ts; - 终端输入
tsc demo2.ts,生成demo2.js; - 输入
node demo2.js,输出:修改后的用户: { id: 1001, name: '小红', age: 21, email: 'xiaohong@163.com' }; - 测试错误:解开注释的
errorUser,执行tsc demo2.ts,查看 TS 报错信息,理解接口的约束作用。
常见问题
- 报错"缺少属性 'age'":检查对象是否漏写了 interface 中规定的必填属性;
- 报错"类型 'string' 不能赋值给类型 'number'":属性类型写错了,比如 id 写成了字符串;
- 想给对象加额外属性:比如给 user 加一个
phone: "123456",会报错,因为 interface 中没有定义 phone 属性(TS 严格约束对象结构)。
案例三:数组类型实战(项目高频,必掌握)
需求说明
定义一个"用户列表"数组,要求数组中的每一项都是"用户对象"(符合案例二中的 User 接口);再写一个函数,筛选出列表中的成年用户(年龄 ≥ 18),确保筛选后的数组类型依然正确。
完整可运行代码
typescript
// 1. 复用案例二的 User 接口(不用重复定义,直接用)
interface User {
id: number;
name: string;
age: number;
email: string;
isVip?: boolean;
}
// 2. 定义用户列表数组:数组的每一项必须是 User 类型
// 写法1:User[] ------ 最常用,简洁
const userList: User[] = [
{ id: 1001, name: "小红", age: 20, email: "xiaohong@163.com" },
{ id: 1002, name: "小李", age: 17, email: "xiaoli@163.com" },
{ id: 1003, name: "小张", age: 25, email: "xiaozhang@163.com" },
{ id: 1004, name: "小王", age: 16, email: "xiaowang@163.com" },
];
// 写法2:Array<User> ------ 和 User[] 完全等价,写法不同而已
// const userList: Array<User> = [ ... ];
// 3. 定义函数:筛选成年用户(年龄 ≥ 18)
// 函数参数:list(必须是 User[] 类型,即"每一项都是 User 的数组")
// 函数返回值:User[](返回的依然是"每一项都是 User 的数组")
function filterAdultUser(list: User[]): User[] {
// 筛选逻辑:保留 age ≥ 18 的用户
return list.filter((user) => user.age >= 18);
}
// 4. 调用函数,测试效果
const adultList = filterAdultUser(userList);
// 打印筛选后的成年用户
console.log("成年用户列表:", adultList);
// 错误演示(注释掉,否则报错)
// const errorList: User[] = [
// { id: 1005, name: "小周", age: "19", email: "xiaozhou@163.com" },
// // 报错:age 是字符串,不符合 User 接口的 number 类型
// { id: 1006, name: "小吴", email: "xiaowu@163.com" }
// // 报错:缺少 age 属性
// ];
逐行解析
-
const userList: User[] = [ ... ]:User[]是 TS 数组类型的写法,表示"这个数组的每一项都必须是 User 类型",如果数组中有一项不符合 User 接口,TS 会报错。 -
Array<User>:和User[]完全等价,只是写法不同,User[]更简洁,项目中更常用。 -
function filterAdultUser(list: User[]): User[] { ... }:- 参数
list: User[]:约束传入的必须是"每一项都是 User 的数组"; - 返回值
: User[]:约束函数返回的必须是"每一项都是 User 的数组",确保筛选后的数据类型依然正确; list.filter(...):数组的 filter 方法,筛选出符合条件的用户,TS 会自动推导筛选后的数组类型,但我们手动标注返回值类型,更规范、更安全。
- 参数
-
错误演示:
errorList中有两个错误:① 小周的 age 是字符串(应该是数字);② 小吴缺少 age 属性,所以 TS 报错,确保数组中每一项都符合要求。
运行步骤
- 新建文件
demo3.ts,复制完整代码; - 终端输入
tsc demo3.ts,生成demo3.js; - 输入
node demo3.js,输出:成年用户列表: [ { id: 1001, ... }, { id: 1003, ... } ](只保留年龄≥18的用户)。
常见问题
- 报错"类型 'string' 不能赋值给类型 'number'":数组中某一项的属性类型写错了;
- 数组中漏写必填属性:比如某用户没有 email,TS 会报错;
- 想定义"混合类型数组":比如
const list: (string | number)[] = [1, "a"],可以用联合类型|(案例四会详细讲)。
案例四:联合类型实战(解决"一个变量多种类型"场景)
需求说明
定义一个"用户ID"变量,ID 可能是数字(比如 1001),也可能是字符串(比如 "a1001");再写一个函数,根据 ID 查找用户,确保函数参数能接收数字或字符串类型的 ID,且返回值类型正确。
完整可运行代码
typescript
// 1. 复用 User 接口
interface User {
id: number;
name: string;
age: number;
email: string;
isVip?: boolean;
}
// 2. 定义用户列表
const userList: User[] = [
{ id: 1001, name: "小红", age: 20, email: "xiaohong@163.com" },
{ id: 1002, name: "小李", age: 17, email: "xiaoli@163.com" },
{ id: 1003, name: "小张", age: 25, email: "xiaozhang@163.com" },
];
// 3. 定义联合类型:ID 可以是 number 或 string
// 写法:type 别名 + 联合类型(| 表示"或")
type UserId = number | string;
// 4. 定义函数:根据 ID 查找用户
// 函数参数:id(类型是 UserId,即 number 或 string)
// 函数返回值:User | undefined(找到返回 User,找不到返回 undefined)
function findUserById(id: UserId): User | undefined {
// 查找逻辑:如果 ID 是数字,直接匹配;如果是字符串,转成数字再匹配
return userList.find((user) => {
// 类型缩小:判断 id 的类型,避免类型错误
if (typeof id === "string") {
// 如果是字符串,去掉开头的非数字字符,转成数字
return user.id === Number(id.replace(/\D/g, ""));
} else {
// 如果是数字,直接匹配
return user.id === id;
}
});
}
// 5. 调用函数,测试两种 ID 类型
// 测试数字 ID
const user1 = findUserById(1001);
console.log("数字 ID 查找结果:", user1);
// 测试字符串 ID
const user2 = findUserById("a1002");
console.log("字符串 ID 查找结果:", user2);
// 测试不存在的 ID(返回 undefined)
const user3 = findUserById(9999);
console.log("不存在的 ID 查找结果:", user3);
逐行解析
-
type UserId = number | string;:这是 TS 的"类型别名"+"联合类型":type:用来给类型起一个别名,简化复杂类型(比如联合类型、对象类型);|:联合类型,表示"这个类型可以是其中任意一种",这里UserId表示"要么是数字,要么是字符串"。
-
function findUserById(id: UserId): User | undefined { ... }:- 参数
id: UserId:约束传入的 ID 可以是数字或字符串; - 返回值
: User | undefined:联合类型,表示"找到用户返回 User 类型,找不到返回 undefined",TS 会自动提醒你处理 undefined 情况(避免报错)。
- 参数
-
if (typeof id === "string") { ... }:这是 TS 的"类型缩小",通过判断变量的类型,让 TS 知道"当前代码块中,id 是字符串类型",从而避免类型错误(比如不能直接把字符串和数字比较)。 -
id.replace(/\D/g, ""):处理字符串 ID 的逻辑(比如把 "a1002" 转成 "1002"),再转成数字,和用户的 id(数字)匹配。
运行步骤
-
新建文件
demo4.ts,复制完整代码; -
终端输入
tsc demo4.ts,生成demo4.js; -
输入
node demo4.js,输出:- 数字 ID 查找结果:{ id: 1001, ... }
- 字符串 ID 查找结果:{ id: 1002, ... }
- 不存在的 ID 查找结果:undefined
常见问题
- 忘记做类型缩小:比如直接写
user.id === id,TS 会报错,因为 id 可能是字符串,不能直接和数字比较; - 联合类型赋值错误:比如
let id: UserId = true,会报错,因为 true 既不是数字也不是字符串; - 返回值未处理 undefined:比如直接写
console.log(user1.name),TS 会提醒你"user1 可能是 undefined",需要先判断if (user1) { ... }。
案例五:泛型入门实战(简化重复代码,高级入门)
需求说明
写一个"通用打印函数",既能打印字符串、数字,也能打印用户对象、用户列表,不用重复写多个函数;用泛型实现"一次定义,多类型复用",确保打印的内容类型正确。
完整可运行代码
php
// 1. 复用 User 接口
interface User {
id: number;
name: string;
age: number;
email: string;
}
// 2. 定义泛型函数:通用打印函数
// <T>:泛型参数,相当于"类型占位符",调用函数时再指定具体类型
// 参数 arg:类型是 T(即调用时指定的类型)
// 返回值:T(返回和参数相同类型的值)
function print<T>(arg: T): T {
console.log("打印内容:", arg);
return arg; // 返回参数,方便链式调用(可选)
}
// 3. 测试泛型函数(多种类型都能使用,不用重复写函数)
// 测试1:打印字符串(T 自动推导为 string)
print("Hello TypeScript");
// 测试2:打印数字(T 自动推导为 number)
print(123456);
// 测试3:打印布尔值(T 自动推导为 boolean)
print(true);
// 测试4:打印用户对象(T 自动推导为 User)
const user: User = { id: 1001, name: "小红", age: 20, email: "xiaohong@163.com" };
print(user);
// 测试5:打印用户列表(T 自动推导为 User[])
const userList: User[] = [user, { id: 1002, name: "小李", age: 17, email: "xiaoli@163.com" }];
print(userList);
// 手动指定泛型类型(可选,一般不用,TS 会自动推导)
print<string>("手动指定类型");
逐行解析(泛型不用怕,简单理解)
-
function print<T>(arg: T): T { ... }:这是泛型函数的基础写法:<T>:泛型参数,相当于一个"类型占位符",你可以把它理解为"一个变量,但存的是类型";arg: T:参数的类型是 T,即"调用函数时,传入的参数是什么类型,T 就是什么类型";: T:返回值的类型是 T,和参数类型一致;- 核心作用:复用代码,不用为 string、number、User 等每种类型都写一个打印函数。
-
测试泛型函数:
- 调用
print("Hello TypeScript")时,TS 自动推导 T 是 string,所以参数必须是 string; - 调用
print(user)时,TS 自动推导 T 是 User,所以参数必须是 User 类型; - 手动指定泛型:
print<string>("手动指定类型"),强制 T 是 string,传入其他类型会报错。
- 调用
-
泛型的优势:如果没有泛型,你需要写 5 个打印函数(printString、printNumber、printUser 等),有了泛型,一个函数就能搞定所有类型,且类型依然安全。
运行步骤
- 新建文件
demo5.ts,复制完整代码; - 终端输入
tsc demo5.ts,生成demo5.js; - 输入
node demo5.js,会依次打印出字符串、数字、布尔值、用户对象、用户列表,且类型都正确。
常见问题
- 泛型参数写错:比如
function print<T>(arg: T): string,返回值类型和 T 不一致,会报错; - 觉得泛型复杂:不用急,入门阶段只要会用这种"自动推导"的泛型函数即可,后续项目中慢慢熟练;
- 手动指定泛型类型错误:比如
print<number>("abc"),会报错,因为指定了 number 类型,却传入了字符串。
附录:TS 环境搭建(5 分钟搞定)
-
安装 Node.js:
- 官网下载:nodejs.org/(选择 LTS 版本,比如 18.x 版本);
- 安装完成后,打开终端,输入
node -v,出现版本号即安装成功。
-
安装 TypeScript(全局安装):
- 终端输入
npm install -g typescript(如果报错,前面加sudo,mac/Linux 系统); - 安装完成后,输入
tsc -v,出现版本号即安装成功。
- 终端输入
-
验证环境:
- 新建
test.ts,写入let a: string = "test"; - 终端输入
tsc test.ts,生成test.js; - 输入
node test.js,无报错即环境正常。
- 新建