学习笔记--入门typescript直接案例开搞

案例一:基础类型实战(最常用,必掌握)

需求说明

定义一个"用户基础信息"变量,包含姓名、年龄、是否是学生,要求用 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

逐行解析(重点!零基础必看)

  1. let userName: string = "小明";: string 是 TS 的类型注解,告诉 TS"这个变量只能存字符串",如果赋值 123true,TS 会直接报错(提前规避错误)。

  2. let userAge: number = 18;: number 约束变量只能存数字,赋值 "18"(字符串)会报错。

  3. let isStudent: boolean = true;: boolean 约束变量只能存 truefalse

  4. function printUserInfo(name: string, age: number, isStu?: boolean): void {}

    1. name: string:函数第一个参数必须是字符串;
    2. age: number:函数第二个参数必须是数字;
    3. isStu?: boolean? 表示"可选参数",可以传,也可以不传,传的话必须是布尔值;
    4. : void:函数没有返回值(如果函数有 return,这里要写返回值的类型,比如 : string)。
  5. if (isStu) {}:因为 isStu 是可选参数,可能为 undefined,所以必须判断,否则会有潜在错误(TS 会提醒你)。

  6. 错误调用演示:注释掉的代码如果解开,TS 会直接报错,这就是 TS 的核心作用------提前检查类型错误,不用等到运行时才发现问题。

运行步骤(手把手教)

  1. 新建文件:在桌面新建文件夹 ts-demo,打开文件夹,新建文件 demo1.ts
  2. 复制代码:将上面的完整代码复制到 demo1.ts 中;
  3. 编译 TS:打开终端(按住 shift+右键,选择"在此处打开终端"),输入tsc demo1.ts,会生成 demo1.js 文件(TS 编译成 JS);
  4. 运行 JS:输入node demo1.js,终端会输出:姓名:小明,年龄:18,身份:学生
  5. 测试错误:解开注释的错误调用代码,再执行 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"
// };

逐行解析(重点!)

  1. interface User { ... }:这是 TS 定义对象类型的核心方式,叫做"接口",作用是规定对象的结构

    1. 里面的每一行都是"属性名: 类型",比如id: number 表示"对象必须有 id 属性,且类型是数字";
    2. isVip?: boolean? 表示可选属性,有没有都可以,不写不会报错;
    3. 如果定义的对象不符合这个接口(比如少属性、属性类型错),TS 会直接报错。
  2. const user: User = { ... }: User 表示"这个 user 对象必须符合 User 接口的格式",里面的属性不能多、不能少,类型不能错。

  3. function updateUserAge(user: User, newAge: number): User { ... }

    1. 第一个参数 user: User:约束传入的参数必须是 User 类型的对象;
    2. 第二个参数 newAge: number:约束传入的新年龄必须是数字;
    3. 返回值 : User:约束函数返回的必须是 User 类型的对象,确保修改后的数据格式依然正确。
  4. 错误演示:注释掉的 errorUser 有两个错误:① id 是字符串(应该是数字);② 缺少 age 属性(接口规定必须有),所以 TS 会报错,提前规避错误。

运行步骤

  1. ts-demo 文件夹中,新建文件 demo2.ts
  2. 复制上面的完整代码到 demo2.ts
  3. 终端输入 tsc demo2.ts,生成 demo2.js
  4. 输入 node demo2.js,输出:修改后的用户: { id: 1001, name: '小红', age: 21, email: 'xiaohong@163.com' }
  5. 测试错误:解开注释的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 属性
// ];

逐行解析

  1. const userList: User[] = [ ... ]User[] 是 TS 数组类型的写法,表示"这个数组的每一项都必须是 User 类型",如果数组中有一项不符合 User 接口,TS 会报错。

  2. Array<User>:和 User[] 完全等价,只是写法不同,User[] 更简洁,项目中更常用。

  3. function filterAdultUser(list: User[]): User[] { ... }

    1. 参数 list: User[]:约束传入的必须是"每一项都是 User 的数组";
    2. 返回值 : User[]:约束函数返回的必须是"每一项都是 User 的数组",确保筛选后的数据类型依然正确;
    3. list.filter(...):数组的 filter 方法,筛选出符合条件的用户,TS 会自动推导筛选后的数组类型,但我们手动标注返回值类型,更规范、更安全。
  4. 错误演示:errorList 中有两个错误:① 小周的 age 是字符串(应该是数字);② 小吴缺少 age 属性,所以 TS 报错,确保数组中每一项都符合要求。

运行步骤

  1. 新建文件 demo3.ts,复制完整代码;
  2. 终端输入 tsc demo3.ts,生成 demo3.js
  3. 输入 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);

逐行解析

  1. type UserId = number | string;:这是 TS 的"类型别名"+"联合类型":

    1. type:用来给类型起一个别名,简化复杂类型(比如联合类型、对象类型);
    2. |:联合类型,表示"这个类型可以是其中任意一种",这里 UserId 表示"要么是数字,要么是字符串"。
  2. function findUserById(id: UserId): User | undefined { ... }

    1. 参数 id: UserId:约束传入的 ID 可以是数字或字符串;
    2. 返回值 : User | undefined:联合类型,表示"找到用户返回 User 类型,找不到返回 undefined",TS 会自动提醒你处理 undefined 情况(避免报错)。
  3. if (typeof id === "string") { ... }:这是 TS 的"类型缩小",通过判断变量的类型,让 TS 知道"当前代码块中,id 是字符串类型",从而避免类型错误(比如不能直接把字符串和数字比较)。

  4. id.replace(/\D/g, ""):处理字符串 ID 的逻辑(比如把 "a1002" 转成 "1002"),再转成数字,和用户的 id(数字)匹配。

运行步骤

  1. 新建文件 demo4.ts,复制完整代码;

  2. 终端输入 tsc demo4.ts,生成 demo4.js

  3. 输入 node demo4.js,输出:

    1. 数字 ID 查找结果:{ id: 1001, ... }
    2. 字符串 ID 查找结果:{ id: 1002, ... }
    3. 不存在的 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>("手动指定类型");

逐行解析(泛型不用怕,简单理解)

  1. function print<T>(arg: T): T { ... }:这是泛型函数的基础写法:

    1. <T>:泛型参数,相当于一个"类型占位符",你可以把它理解为"一个变量,但存的是类型";
    2. arg: T:参数的类型是 T,即"调用函数时,传入的参数是什么类型,T 就是什么类型";
    3. : T:返回值的类型是 T,和参数类型一致;
    4. 核心作用:复用代码,不用为 string、number、User 等每种类型都写一个打印函数。
  2. 测试泛型函数:

    1. 调用 print("Hello TypeScript") 时,TS 自动推导 T 是 string,所以参数必须是 string;
    2. 调用 print(user) 时,TS 自动推导 T 是 User,所以参数必须是 User 类型;
    3. 手动指定泛型:print<string>("手动指定类型"),强制 T 是 string,传入其他类型会报错。
  3. 泛型的优势:如果没有泛型,你需要写 5 个打印函数(printString、printNumber、printUser 等),有了泛型,一个函数就能搞定所有类型,且类型依然安全。

运行步骤

  1. 新建文件 demo5.ts,复制完整代码;
  2. 终端输入 tsc demo5.ts,生成 demo5.js
  3. 输入 node demo5.js,会依次打印出字符串、数字、布尔值、用户对象、用户列表,且类型都正确。

常见问题

  • 泛型参数写错:比如 function print<T>(arg: T): string,返回值类型和 T 不一致,会报错;
  • 觉得泛型复杂:不用急,入门阶段只要会用这种"自动推导"的泛型函数即可,后续项目中慢慢熟练;
  • 手动指定泛型类型错误:比如 print<number>("abc"),会报错,因为指定了 number 类型,却传入了字符串。

附录:TS 环境搭建(5 分钟搞定)

  1. 安装 Node.js:

    1. 官网下载:nodejs.org/(选择 LTS 版本,比如 18.x 版本);
    2. 安装完成后,打开终端,输入 node -v,出现版本号即安装成功。
  2. 安装 TypeScript(全局安装):

    1. 终端输入 npm install -g typescript(如果报错,前面加 sudo,mac/Linux 系统);
    2. 安装完成后,输入tsc -v,出现版本号即安装成功。
  3. 验证环境:

    1. 新建 test.ts,写入 let a: string = "test"
    2. 终端输入 tsc test.ts,生成 test.js
    3. 输入 node test.js,无报错即环境正常。
相关推荐
To_OC6 小时前
别再串行写 await 了,Promise.all 才是并行请求的正确打开方式
前端·javascript·promise
vipbic7 小时前
中后台越做越乱后,我用插件化把它救回来了
前端·vue.js
Hyyy7 小时前
Computer Use 适合做什么,不适合做什么——一次真实使用后的思考
前端
小和尚同志7 小时前
前端 AI 单元测试思考与落地
前端·人工智能·aigc
invicinble9 小时前
c端系统,其实更像一个信息展示平台
前端
李姆斯9 小时前
管理是否可以被完全量化
前端·产品经理·团队管理
名字还没想好☜10 小时前
Next.js 中间件实战:鉴权、重定向与 A/B 分流
开发语言·前端·javascript·中间件·react·next.js
广州灵眸科技有限公司10 小时前
瑞芯微RV1126B开发板(EASY-EAI-PI2) INI文件操作
java·前端·javascript·网络·人工智能
kaiking_g11 小时前
vue项目中,静态导入 vs 动态导入 详解
前端·javascript·vue.js
江华森11 小时前
Web 开发基础:HTTP 与 REST
前端·网络协议·http