当前位置: 首页 > news >正文

TypeScript 入门教程:01 — 为什么需要类型

1. 你以前怎么惨

你写了一个购物车计算函数。两个参数:商品单价和数量。

// shopping.jsfunctioncalcTotal(price,qty){returnprice*qty;}

同事 A 从 API 拿到价格,是一个字符串"19.90"。他调用calcTotal("19.90", 3)。JS 把字符串悄悄转成数字,得到59.7。碰巧对了。

同事 B 从另一个接口拿到数量,也是字符串"5"。他调用calcTotal(12.5, "5")。又碰巧对了,JS 又帮你转了。

然后同事 C 来了。他在表单里取价格,用户不小心输入了"abc"calcTotal("abc", 3)返回NaN。没有报错,没有红字,购物车总价显示 “NaN”。你翻了几百行代码才找到这一行。

更惨的是:同事 D 把参数顺序写反了——calcTotal(3, 19.90)。JS 照样算出一个数字,只是结果是错的。没人告诉你。

JS 的问题:它太"好心"了。什么东西都帮你悄悄转换,错了也不说。

2. 先看一眼烂代码

下面这些代码在 JS 里全部能跑。没有一个报错。结果全是错的。

// 数字 + 字符串 = 字符串拼接,不是加法"5"+3;// -> "53"(你想要 8)// 数学运算碰上字符串 = NaN,但 JS 不报错Math.max("hello","world");// -> NaN// 访问不存在的属性 = undefined,继续用 undefined 调用方法 = 炸constuser={name:"小明"};user.email.toUpperCase();// ❌ TypeError: Cannot read properties of undefined// 对象拼错字段名 = undefined,没人提醒你functiongetAge(person){returnperson.gae;// 拼错了 age ——返回 undefined}getAge({name:"张三",age:30});// -> undefined(应该是 30)// 数组方法用在字符串上——字符被当成元素遍历"hello".map(ch=>ch.toUpperCase());// ❌ TypeError: "hello".map is not a function

你看出来了吗?每一个错误都是运行到那一行才炸。有些甚至不炸,只是悄悄地错了。你的测试覆盖率要 100% 才能抓到全部问题——现实中没有人做到。

3. 用新语法修好它

3.1 给参数贴标签:stringnumberboolean

JS 的方式:参数叫name,但鬼知道它是什么。

functiongreet(name,age,isMember){return`${name}${age}${isMember}`;}greet(42,"小明","yes");// 全错,不报错

TS 的方式:参数后面加冒号和类型名。就像快递包裹上贴标签。

functiongreet(name:string,age:number,isMember:boolean):string{return`${name}${age}${isMember}`;}greet(42,"小明","yes");// ❌ 编译就报错:第一个参数应该是 string

类型标注就是给每个变量发一张身份证。身份证上的"姓名"栏只能写文字,"年龄"栏只能写数字。填错了,编译器直接拒绝。

三个基本类型:

  • : string— 文字。"hello""张三"
  • : number— 数字。整数、小数、NaNInfinity都是 number。
  • : booleantruefalse。没有第三种。

3.2 描述对象的形状:interface

JS 里对象是"麻袋"——什么东西都能往里塞。

functionprintUser(user){console.log(user.name,user.age);}// 下面三个调用都不报错,但只有第一个有意义:printUser({name:"小明",age:18});// 对的printUser({name:"小明"});// age = undefinedprintUser({nam:"小明",age:18,xxx:"垃圾"});// name 拼错 + 多余字段

TS 用interface描述对象的"形状"——有哪些字段,各自什么类型。

interfaceUser{name:string;age:number;}functionprintUser(user:User):void{console.log(user.name,user.age);// TS 知道 user 一定有 name 和 age}printUser({name:"小明",age:18});// ✅// printUser({ name: "小明" }); // ❌ 缺 age// printUser({ name: "小明", age: 18, xxx: 1 }); // ❌ 多余字段

interface就像一张表格模板。你定义了三个列:姓名、年龄、邮箱。任何人填表时必须每列都填,不能多填一列,也不能少填一列。

类比:interface= 快递单模板。"收件人"栏、"电话"栏、"地址"栏——缺一不可,不能多贴一张便利贴。

3.3 可选字段:?

现实中的表单不会每个字段都必填。邮箱可能没有,昵称可能不写。

interfaceProfile{name:string;// 必填email?:string;// 可选:有就填,没有就不填}functionshowProfile(p:Profile):string{// TS 强制你处理 email 可能是 undefined 的情况return`${p.name}(${p.email??"无邮箱"})`;}showProfile({name:"小明"});// ✅ 不填 email 也可以showProfile({name:"小红",email:"hi@x.com"});// ✅ 填了更好

?= 表单上的"(选填)"标记。你不填它,值是undefined。TS 知道它可能是undefined,所以你不先判断就不能直接用(比如直接.toUpperCase()会报编译错)。

3.4 数组与字面量类型

数组number[]表示"这个篮子里只能装苹果(数字)"。不能塞一根香蕉(字符串)。

functionsum(nums:number[]):number{returnnums.reduce((a,b)=>a+b,0);}sum([1,2,3]);// ✅// sum(["1", "2"]); // ❌ 字符串不能放进 number[]

字面量类型:一个变量不只是string,它只能是"dark""light"

// JS:theme 是 string,拼错了也不报错functionsetTheme(theme){if(theme==="dark"){/* ... */}}setTheme("drak");// 悄悄错了
// TS:theme 只能是这两个值之一typeTheme="dark"|"light";functionsetTheme(theme:Theme):void{/* ... */}// setTheme("drak"); // ❌ 编译报错:"drak" 不在 "dark" | "light" 中

|读作"或"。"dark" | "light"= 只能是 dark 或 light。

as const:像超级胶水,让 TS 把值当类型用,而不是宽泛的string/number

constcfg={theme:"dark",port:3000}asconst;// cfg.theme 类型 = "dark"(不是 string)// cfg.port 类型 = 3000(不是 number)// 整个对象变成只读,不可修改

4. 什么时候不该用

不要给一个 10 行的临时脚本加上全套interface。类型标注适合"会被多次调用"、“有其他人维护”、"数据流过多个函数"的场景。

  • 写完就扔的脚本:别加类型,浪费时间。
  • JSON 数据本身结构不明:比如解析一个你完全没见过的第三方 API 返回。先用unknown而不是硬猜一个 interface。
  • as const不要用在"用户会修改"的数据上。配置常量用它,表单输入别用。

5. 小结

语法一句话示例
: string这个变量是文字name: string
: number这个变量是数字age: number
: boolean这个变量是 true/falsedone: boolean
interface描述对象长什么样interface User { name: string }
?这个字段可以不填email?: string
number[]数字组成的数组scores: number[]
"a" | "b"值只能是这几个之一dir: "left" | "right"
as const冻结值,让 TS 知道精确值{ port: 3000 } as const
http://www.jsqmd.com/news/1172008/

相关文章:

  • 不锈钢非标零件加工,为什么容易变形?
  • Gemini Omni Flash API:对话式视频编辑技术解析与实践指南
  • 2026年7月最新烟台真力时官方售后客户服务电话及线下网点地址 - 亨得利官方服务中心
  • C++手动解析BMP格式与图像旋转180度实现详解
  • 3款学术写作工具深度对比:Writefull vs Quillbot vs Wordvice AI 在 SCI 论文润色中的实测
  • 【SkyWalking从入门到精通】第36篇:OAP Server启动流程解析:从一行命令到一个可观测大脑的诞生
  • Rust Trait核心机制解析:从零成本抽象到动态分发实战
  • 测试用例设计误区解析:单缺陷与多缺陷假设在3类输入场景下的应用边界
  • Arduino IDE 2.3.2 + STM32duino 2.8.1 离线安装:HFS本地服务器搭建与5个依赖包配置
  • ADI ICE-2000 vs ICE-1000 对比评测:46MHz vs 5MHz 主频下编译效率实测
  • 县域中心能为当地做什么?OID赋能县域数字经济全解析
  • 凸优化问题 KKT条件 3个关键应用:SVM、投资组合与工程设计的充要性验证
  • AI智能体安全防御实战:技能投毒攻击原理与悬镜灵境AIDR毫秒级拦截方案
  • 企业获客陷入瓶颈?沃创云 AI 获客系统帮企业破局增收
  • 现代C++高性能开发实战:智能指针、移动语义与并发编程深度解析
  • 适配2024提质增效要求 高校智慧学生社区1256+N建设逻辑全拆解
  • Unity NavMeshAgent卡墙穿模问题:5种解决方案与调试技巧
  • 3 种量化系统 IPC 方案对比:QuantFabric 共享内存 vs ZeroMQ vs gRPC 延迟实测
  • VSCode 字体配置优化:3大区域(编辑器/终端/UI)字体搭配与性能实测
  • Revit模型轻量化导入Unity全流程:从BIM到实时渲染的性能优化指南
  • mdBook vs GitBook:5个维度对比,为你的技术文档选型
  • Anaconda 2024.10 虚拟环境包迁移:pip freeze 与 conda list --export 双列表实战
  • 实践1|用 Claude Code 逆向了一个陌生业务模块
  • ELF 重定位实战:5 个 LinkLab 实验阶段解析符号表与节偏移计算
  • AI Agent开发实战:从原理到多Agent系统架构设计
  • 具身智能专家学习路线图
  • VS Code 1.86 粘性滚动深度配置:3个关键参数优化与终端集成实战
  • TLA2518与PIC18F86J16构建高精度数据采集系统
  • IEEE 754 单精度浮点数转换实战:3个典型十进制数(含0.15625)的完整二进制推导
  • 动画技术分析:从Netflix《缎带骑士》学习本地处理工作流