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 里全部能跑。没有一个报错。结果全是错的。// 数字 字符串 字符串拼接不是加法53;// - 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(chch.toUpperCase());// ❌ TypeError: hello.map is not a function你看出来了吗每一个错误都是运行到那一行才炸。有些甚至不炸只是悄悄地错了。你的测试覆盖率要 100% 才能抓到全部问题——现实中没有人做到。3. 用新语法修好它3.1 给参数贴标签string、number、booleanJS 的方式参数叫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— 数字。整数、小数、NaN、Infinity都是 number。: boolean—true或false。没有第三种。3.2 描述对象的形状interfaceJS 里对象是麻袋——什么东西都能往里塞。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:hix.com});// ✅ 填了更好? 表单上的(选填)标记。你不填它值是undefined。TS 知道它可能是undefined所以你不先判断就不能直接用比如直接.toUpperCase()会报编译错。3.4 数组与字面量类型数组number[]表示这个篮子里只能装苹果数字。不能塞一根香蕉字符串。functionsum(nums:number[]):number{returnnums.reduce((a,b)ab,0);}sum([1,2,3]);// ✅// sum([1, 2]); // ❌ 字符串不能放进 number[]字面量类型一个变量不只是string它只能是dark或light。// JStheme 是 string拼错了也不报错functionsetTheme(theme){if(themedark){/* ... */}}setTheme(drak);// 悄悄错了// TStheme 只能是这两个值之一typeThemedark|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: booleaninterface描述对象长什么样interface User { name: string }?这个字段可以不填email?: stringnumber[]数字组成的数组scores: number[]a | b值只能是这几个之一dir: left | rightas const冻结值让 TS 知道精确值{ port: 3000 } as const