✨TypeScript快速入门第一篇:从基础到 any、unknown、never 的实战解析

TypeScript 入门:从基础到 any、unknown、never 的实战解析

作为前端开发者,你可能听过 "JS 够用了,为什么还要学 TS?"------ 但当项目代码量突破万行、多人协作频繁时,JS 的 "灵活性" 会逐渐变成 "隐形炸弹":传错参数类型、改漏对象属性、接口返回结构混乱... 这些问题往往要到运行时才暴露,排查成本极高。

而 TypeScript(简称 TS)的核心价值,就是给 JS 加上 "类型安全网":提前在编译阶段拦截类型错误,让代码更易维护、协作更高效。今天我们就从 TS 基础入手,再深入拆解三个容易混淆的特殊类型:any、unknown、never,帮你搞懂 "什么时候该用什么类型"。

一、TS 基础:先学会 "给 JS 加类型"

TS 是 JS 的超集,所有 JS 代码能直接在 TS 中运行,我们要做的核心操作,就是给变量、函数、对象 "标注类型"------ 让 TS 知道 "这个数据应该是什么样子"。

1. 变量的类型标注

语法很简单:let 变量名: 类型 = 值。TS 甚至能 "自动推论类型"(简单场景不用手动标),但复杂场景建议显式标注,可读性更强。

typescript 复制代码
// 1. 基础类型:string/number/boolean
let username: string = "张三"; // 字符串
let age: number = 28; // 数字(整数/小数都支持)
let isVip: boolean = true; // 布尔值(仅true/false)
// 2. 数组类型:两种写法
let hobbies: string[] = ["打球", "编程"]; // 推荐:类型+[]
let scores: Array<number> = [90, 85, 95]; // 泛型写法(后续会讲)
// 3. 自动推论:简单场景可省略类型标注
let city = "北京"; // TS自动推论city是string类型
city = 123; // 报错:不能将number赋给string

2. 函数的类型标注

重点标注 "参数类型" 和 "返回值类型",避免传错参数或误解返回结果。

typescript 复制代码
// 语法:function 函数名(参数: 类型): 返回值类型 { ... }
function add(a: number, b: number): number {
  return a + b; // 正确:返回number
}
// 可选参数:加?,表示可传可不传
function greet(name?: string): string {
  return name ? `Hi ${name}` : "Hi 陌生人";
}
// 无返回值:用void标注(表示"正常执行完,但没返回值")
function logMsg(msg: string): void {
  console.log(msg); // 不用return,或return undefined
}

3. 对象的类型约束(接口 Interface)

当需要定义 "对象结构" 时(比如后端返回的用户数据),用interface明确属性名和类型,避免漏传或错传属性。

typescript 复制代码
// 定义用户接口:规定对象必须有哪些属性
interface User {
  id: string; // 必选属性
  name: string;
  age?: number; // 可选属性(加?)
  readonly role: string; // 只读属性(不能修改)
}
// 使用接口:对象必须符合接口结构
const user: User = {
  id: "u123",
  name: "李四",
  role: "user" // 只读,后续不能改 user.role = "admin" 会报错
};

二、深入解析:any、unknown、never 三个特殊类型

前面的基础类型(string/number 等)很直观,但实际开发中会遇到 "不确定类型" 的场景(比如后端返回的未知数据、第三方库的复杂类型),这时候就需要 any、unknown、never 这三个特殊类型 ------ 但它们的用法和风险差异极大,用错了会让 TS 的 "类型安全" 形同虚设。

1. any:"万能但危险" 的类型

定义:表示 "任意类型",TS 会完全放弃对 any 类型的检查 ------ 你可以给 any 变量赋任何值,也可以把 any 变量赋给任何类型。

ini 复制代码
let value: any = "hello";
value = 123; // 不报错:any支持任意类型赋值
value = { id: 1 }; // 仍不报错
// 风险:类型污染
let num: number = value; // 不报错:any能赋给任何类型
num.toFixed(2); // 运行时可能报错:如果value实际是对象,toFixed不存在

使用场景

  • 旧 JS 项目迁移到 TS 时,临时兼容 "无法确定类型" 的代码(过渡方案);
  • 确实需要 "关闭类型检查" 的极端场景(比如动态生成的代码)。

避坑点

  • 禁止 "隐式 any":开启tsconfig.json的strict: true,当变量没标类型且 TS 无法推论时(如function fn(a) {}),会直接报错,避免无意识的类型污染;
  • 能不用就不用:any 会让 TS 的 "提前纠错" 优势失效,优先用后面的 unknown 替代。

2. unknown:"安全的不确定类型"

定义 :同样表示 "不确定类型",但比 any 更安全 ------unknown 变量可以接收任何类型的值,但不能直接赋给其他类型(除了 any 和 unknown),必须先 "确认类型" 才能使用。

ini 复制代码
let value: unknown = "hello";
value = 123; // 不报错:unknown支持接收任意类型
// 1. 直接赋值给其他类型:报错(安全机制)
let num: number = value; // 报错:不能将unknown赋给number
// 2. 先确认类型(类型守卫),再使用:安全
if (typeof value === "number") {
  // 这里TS会自动推论value是number类型
  num = value; // 不报错
  console.log(num.toFixed(2)); // 安全:确定是number
}

常用类型守卫方式

  • typeof:判断基础类型(string/number/boolean 等);
  • instanceof:判断引用类型(Array/Object/ 自定义类等);
  • 自定义函数:判断复杂结构(如是否符合 User 接口)。
javascript 复制代码
// 自定义类型守卫:判断是否为User类型
function isUser(obj: unknown): obj is User {
  return (
    typeof obj === "object" &&
    obj !== null &&
    "id" in obj &&
    "name" in obj &&
    "role" in obj
  );
}
// 使用:先判断,再使用
if (isUser(value)) {
  console.log(value.name); // 安全:确定是User类型
}

使用场景

  • 接收未知类型的数据(如后端接口返回的 JSON、用户输入的动态值);
  • 替代 any,在 "不确定类型" 时保持类型安全。

核心优势:既保留了 "接收任意类型" 的灵活性,又通过 "类型守卫" 避免了类型污染,是 any 的安全替代方案。

3. never:"不存在的底层类型"

定义:表示 "不存在的类型",是 TS 的底层类型 ------ 任何值都不能赋给 never,但 never 可以赋给任何类型(类比 "空集是任何集合的子集")。

never 的场景很特殊,主要用于两种情况:

情况 1:函数永远不会正常执行结束

比如函数抛错、死循环 ------ 因为函数永远到不了 "返回" 这一步,所以返回值类型是 never(不是 void,void 表示 "正常执行完但没返回值")。

typescript 复制代码
// 1. 抛错函数:执行到throw就中断,永远不返回
function error(msg: string): never {
  throw new Error(msg); // 抛错后,函数没机会返回
  console.log("这里永远到不了"); // 死代码,TS会提示
}
// 2. 死循环函数:永远不结束
function infiniteLoop(): never {
  while (true) {} // 无限循环,永远不返回
}
情况 2:类型判断 "穷尽所有可能" 后的剩余类型

当你用联合类型做判断时,如果已经覆盖了所有可能的类型,最后 else 分支的类型就是 never------ 这是 TS 的 "穷尽检查" 能力,能帮你避免漏判。

typescript 复制代码
// 定义联合类型:只有两个可能
type Status = "success" | "fail";
function handleStatus(status: Status) {
  if (status === "success") {
    console.log("成功");
  } else if (status === "fail") {
    console.log("失败");
  } else {
    // 此时status的类型是never:因为Status的所有可能都被覆盖了
    // 如果后续给Status加了新值(如"pending"),这里会报错,提醒你补判断
    const _exhaustiveCheck: never = status;
    throw new Error(`未处理的状态:${_exhaustiveCheck}`);
  }
}

使用场景

  • 标注 "会中断执行的函数"(抛错、死循环);
  • 联合类型的 "穷尽检查",避免漏判类型(尤其适合枚举或固定选项的场景)。

三、对比总结:any、unknown、never 该怎么选?

类型 核心特点 安全程度 适用场景
any 放弃类型检查,可赋给任何类型 极低 旧项目迁移、极端需关闭检查的场景
unknown 需先确认类型才能使用,不能直接赋值给其他类型 接收未知类型数据(如接口返回、用户输入)
never 不存在的类型,任何值不能赋给它 极高 函数中断执行、联合类型穷尽检查

一句话选型建议

  • 不确定类型时,优先用unknown(安全),别用any(危险);
  • 函数抛错或死循环时,用never标注返回值;
  • 联合类型判断时,用never做穷尽检查,避免漏判。

四、最后:TS 入门的核心原则

  1. 别追求 "全学会再用" :TS 知识点多,但前端开发常用的只有 "基础类型 + 接口 + unknown + 泛型基础",先掌握这些就能应对 80% 场景;
  1. 开启 strict 模式:tsconfig.json的strict: true能强制你写更规范的类型,避免隐式 any 等坑;
  1. 类型是 "辅助工具" 不是 "束缚" :TS 的目标是帮你少踩坑,不是让你花大量时间写复杂类型 ------ 能清晰表达意图的类型就是好类型。

如果刚开始用 TS 觉得 "麻烦",别担心:写过 1-2 个小项目后,你会发现 "提前标注类型" 比 "后期排查类型 bug" 省力多了。希望这篇文章能帮你快速入门 TS,避开 any、unknown、never 的混淆坑~

相关推荐
CodeSheep3 分钟前
VS 2026 正式发布,王炸!
前端·后端·程序员
无奈何杨4 分钟前
CoolGuard事件查询增加策略和规则筛选,条件结果展示
前端·后端
梦里不知身是客117 分钟前
正则表达式常见的介绍
前端·javascript·正则表达式
初学小白...24 分钟前
HTML知识点
前端·javascript·html
鹏多多26 分钟前
flutter睡眠与冥想数据可视化神器:sleep_stage_chart插件全解析
android·前端·flutter
艾小码37 分钟前
Vue3 脚本革命:<script setup> 让你的代码简洁到飞起!
前端·javascript·vue.js
IT_陈寒1 小时前
Python 3.12新特性解析:10个让你代码效率提升30%的实用技巧
前端·人工智能·后端
故厶1 小时前
webpack实战
前端·javascript·webpack
_果果然1 小时前
你真的懂递归吗?没那么复杂,但也没那么简单
前端·javascript
菜泡泡@2 小时前
仓库地图vue-grid-layout
前端·javascript·vue.js