TypeScript学习-第2章:基础类型
各位前端工友们,上一章咱们搭好了TS的"作战基地",跑通了第一个程序,算是跨进了TS的大门。这一章咱们要解锁核心技能------给变量"贴标签",也就是TS的基础类型体系。
要是把TS比作"代码安全卫士",那基础类型就是卫士的"身份识别系统",每个变量都得亮明身份,才能被卫士精准管控。咱们不搞术语轰炸,就用"贴标签"的逻辑,把这些类型一个个聊透,还会揪出那些容易踩坑的边界场景。
一、原始类型:变量的"基础身份证"
原始类型是TS最基础的类型集合,对应JS里的七种原始值,相当于给变量贴"基础款标签",明确它最本质的身份。每一种都有专属用法和注意点,咱们逐个拆解:
-
string(字符串) :专门管文本内容,用单引号、双引号或反引号包裹都能识别。标签写法很简单:
let name: string = "前端打工仔";。这里要提一嘴,反引号的模板字符串也完全兼容,比如const desc: string =我在学${name}的TS教程;,TS会自动识别为字符串类型。 -
number(数字) :包揽所有数字,不管是整数、小数,还是NaN、Infinity(无穷大),都归它管。标签写法:
let age: number = 25;。重点避坑:很多新手以为NaN是"非数字"就不属于number,其实TS里NaN的类型就是number,只是它是一个"异常数字",这点后续实践部分会细说。 -
boolean(布尔值) :最纯粹的类型,只有两个值------true和false,没有中间态。标签写法:
let isStudy: boolean = true;。别踩坑:JS里用0、1代替布尔值的操作,在TS里会被警告,比如let flag: boolean = 0;会直接标红,强制你规范赋值。 -
null 和 undefined :这俩是"特殊空值兄弟",但用法有区别。undefined表示"变量声明了但没赋值",null表示"变量赋值了,但值是空的"。标签写法:
let emptyVal: null = null;、let unassigned: undefined = undefined;。注意:在严格模式(后续tsconfig会讲)下,null和undefined不能赋值给其他类型变量,比如let num: number = null;会报错,避免乱用空值导致bug。 -
symbol(符号) :用来创建唯一值,适合做对象的唯一属性名,防止属性冲突。标签写法:
const key: symbol = Symbol("uniqueKey");。核心特性:每一个symbol值都是独一无二的,哪怕传入相同的描述,两个symbol也不相等,这是它和其他原始类型最大的区别。 -
bigint(大整数) :处理超出number范围的整数(number最大安全整数是2⁵³-1),在数字后面加n即可表示。标签写法:
let bigNum: bigint = 9007199254740991n;。注意:bigint不能和普通number直接运算,比如bigNum + 10会报错,必须转换成同类型才能计算。
总结一下:原始类型是TS类型体系的"基石",用法简单但要记清边界,尤其是NaN、symbol这些特殊情况,别被表面含义误导。
二、类型标注语法:给变量"贴标签"的正确姿势
上一部分咱们已经用到了标注语法,核心逻辑很简单:在变量/常量名后面加:,再跟上对应的类型,格式为let/const 变量名: 类型 = 值;。
这里要讲两个核心点,新手必掌握:
-
标注时机 :不是所有变量都需要手动标注。比如
let num = 10;,TS会自动推断num是number类型,这就是后面要讲的"类型推导"。手动标注主要用在两种场景:一是变量声明和赋值分离时(比如let num: number; num = 10;),二是需要明确约束类型,避免TS推断偏差时。 -
标注原则:精准且不冗余。比如明明是字符串,就别标成any;能靠类型推导搞定的,就不用手动写标注,既保证类型安全,又不增加代码量。
反例提醒:别犯let name: string = 123;这种低级错误,类型和值不匹配,TS会直接红波浪线警告,相当于卫士当场拦住你:"这标签和实物对不上!"
三、特殊类型:any和unknown的"爱恨情仇"
这俩是TS里的"特殊选手",都能兼容多种类型,但性格完全不同------any是"摆烂型",unknown是"谨慎型",用对了能提高效率,用错了就会失去TS的类型保护。
1. any:彻底关闭类型校验
给变量标上any,相当于告诉TS:"这变量我来管,你别插手校验"。此时变量可以赋值任何类型,也能调用任何属性/方法,TS完全不拦着。
typescript
let randomVal: any = 10;
randomVal = "字符串"; // 不报错
randomVal = true; // 不报错
randomVal.sayHello(); // 不报错(哪怕没有这个方法,运行时才会崩)
警告:any是"万不得已的选择",滥用会让TS变成"加强版JS",失去类型校验的核心优势。只有对接旧JS代码、不确定变量类型时,才临时用any过渡,千万别把它当常规类型用。
2. unknown:安全的"any平替"
unknown和any一样,能接收任何类型的值,但它更"谨慎"------赋值后不能直接调用属性/方法,必须先做类型判断(类型守卫),确认类型后才能操作,避免运行时错误。
typescript
let safeVal: unknown = 10;
safeVal = "hello"; // 不报错
// safeVal.length; // 报错:不能直接调用属性
if (typeof safeVal === "string") {
console.log(safeVal.length); // 不报错(确认是字符串后可操作)
}
结论:不确定变量类型时,优先用unknown代替any,既保留灵活性,又不丢失类型安全,这才是TS的正确用法。
四、空值类型:void和never的"终极区别"
这俩都是"无值"相关的类型,但适用场景完全不同,新手很容易混淆,咱们用"场景化"方式讲清楚:
1. void:函数"无返回值"的标识
当函数没有明确返回值(或者只返回undefined)时,返回值类型就标为void。它表示"函数执行完后,没有返回有意义的值"。
typescript
// 无返回值,标为void
function printLog(msg: string): void {
console.log(msg);
// 可省略return,默认返回undefined
}
// 明确返回undefined,也可标为void
function getNothing(): void {
return undefined;
}
注意:void只能用在函数返回值上,不能给变量标void(除了特殊场景),比如let val: void = undefined;虽然语法不报错,但毫无意义,属于冗余标注。
2. never:"永不存在的值"的类型
never比void更极端,它表示"函数永远不会执行完",或者"变量永远不会有值"。常见场景有两种:
typescript
// 1. 函数抛出错误,永远不会执行到结尾
function throwError(msg: string): never {
throw new Error(msg); // 抛出错误后,函数终止,无返回值
}
// 2. 无限循环,永远不会执行完
function infiniteLoop(): never {
while (true) {
// 循环不终止,函数永远在执行
}
}
never的实用价值:可以用来约束"不可能出现的场景",比如switch语句覆盖所有情况后,默认分支用never兜底,确保逻辑无遗漏,后续高级类型会再拓展用法。
五、类型推导:TS的"智能贴标签"功能
TS不是"死板的守卫",它有"智能大脑",能自动根据赋值给变量贴标签,这就是类型推导。掌握这个功能,能少写很多冗余标注,提高编码效率。
核心规则和场景:
-
声明并赋值时,自动推导类型 :这是最常见的场景,比如
let num = 10;,TS会自动推导num是number类型,后续给num赋值字符串(num = "abc")会报错,和手动标注效果一致。 -
声明和赋值分离时,推导为any :如果只声明变量不赋值,TS会默认推导为any,失去类型校验。比如
let num; num = 10; num = "abc";不报错,因为num被推导为any,所以尽量避免这种写法,要么手动标注类型,要么声明时就赋值。 -
函数返回值自动推导 :函数没有手动标注返回值类型时,TS会根据return语句推导。比如
function add(a: number, b: number) { return a + b; },TS会自动推导返回值是number类型。但复杂函数建议手动标注返回值,让代码更清晰,也能避免推导偏差。
小技巧:类型推导是"辅助工具",不是"万能工具"。简单场景靠推导,复杂场景(比如函数嵌套、多分支返回)手动标注,平衡效率和可读性。
六、实践:基础类型的边界场景避坑
理论学完,必须落地到实践,尤其是那些容易踩坑的边界场景,提前吃透能少走很多弯路。咱们聚焦几个高频坑点:
1. NaN和Infinity的类型问题
前面提过,NaN和Infinity都属于number类型,但它们是"异常值",使用时要注意判断:
typescript
let num: number = NaN;
console.log(typeof num); // 输出"number"
// 判断NaN不能用===,要用isNaN方法
if (isNaN(num)) {
console.log("这是一个异常数字");
}
let inf: number = Infinity;
console.log(inf > 1000000); // 输出true
2. null和undefined的赋值边界
在非严格模式下,null和undefined可以赋值给其他原始类型,但严格模式下会报错。为了类型安全,建议开启严格模式(后续tsconfig配置),同时避免直接赋值:
typescript
// 非严格模式下不报错,严格模式下报错
let str: string = null;
// 正确用法:如果变量可能为空,用联合类型(后续章节讲)
let optionalStr: string | null = null;
3. symbol的唯一特性实践
用symbol做对象属性名,能避免属性冲突,这是它的核心用法:
typescript
const key1: symbol = Symbol("key");
const key2: symbol = Symbol("key");
const obj = {
[key1]: "value1",
[key2]: "value2"
};
console.log(obj[key1] === obj[key2]); // 输出false(两个symbol不同)
七、本章小结:类型是"约束",更是"保障"
这一章咱们吃透了TS的基础类型体系,从原始类型、标注语法,到特殊类型、空值类型、类型推导,再到实践避坑,核心逻辑就一个:类型不是"枷锁",而是"精准约束"。
新手容易陷入两个极端:要么过度依赖any,放弃类型约束;要么逐行标注,导致代码冗余。正确的做法是:用原始类型打基础,用unknown替代any保安全,靠类型推导提效率,复杂场景手动标注强约束。
下一章咱们要升级技能,学习数组、对象、元组这些复合类型,处理更复杂的数据结构。记得把本章的边界场景多练几遍,类型感是靠实践磨出来的~ 有疑问评论区见,咱们一起避坑一起进阶!