基础数据类型
TypeScript是JavaScript的超集,它在JavaScript基础上添加了静态类型系统。TypeScript中的数据类型可以分为基础类型和高级类型,本篇笔记我们介绍TypeScript中的基础数据类型及其用法。
类型注解
在TypeScript中,我们可以通过类型注解来显式声明变量的类型,类型注解使用冒号:后跟类型名称的语法。
let name: string = "Tom";
let age: number = 18;
let isStudent: boolean = true;
当然,TypeScript也具备类型推断能力,在很多情况下我们可以省略类型注解,编译器会根据赋值自动推断类型。
let name = "Tom"; // 自动推断为string类型
let age = 18; // 自动推断为number类型
不过在函数参数、返回值等场景下,显式添加类型注解是推荐的做法,这样可以提高代码的可读性和可维护性。
原始类型
TypeScript支持JavaScript中所有的原始类型,包括string、number、boolean、null、undefined、symbol和bigint。
string 字符串
string类型用于表示文本数据,可以使用单引号、双引号或模板字符串。
let str1: string = "hello";
let str2: string = 'world';
let str3: string = `hello, ${str2}`;
number 数字
number类型用于表示数字,包括整数和浮点数。TypeScript中的number类型和JavaScript一样,都是基于IEEE 754标准的双精度64位浮点数。
let intNum: number = 42;
let floatNum: number = 3.14;
let hexNum: number = 0xff;
let binaryNum: number = 0b1010;
let octalNum: number = 0o744;
boolean 布尔
boolean类型只有两个值:true和false。
let isDone: boolean = false;
let isValid: boolean = true;
null和undefined
null和undefined在TypeScript中既是值也是类型。默认情况下,它们是所有类型的子类型,但在严格模式(strictNullChecks开启)下,它们只能赋值给各自的类型或void类型。
let n: null = null;
let u: undefined = undefined;
在实际开发中,我们通常会开启严格模式,此时如果一个变量可能为null或undefined,需要使用联合类型来声明。
let value: string | null = null;
value = "hello";
symbol
symbol类型用于创建唯一的标识符,常用于对象属性的键。
let sym1: symbol = Symbol();
let sym2: symbol = Symbol("key");
let sym3: symbol = Symbol("key");
console.log(sym2 === sym3); // false,每个Symbol都是唯一的
bigint 大整数
bigint类型用于表示任意精度的整数,适用于需要处理超大整数的场景。
let big1: bigint = 100n;
let big2: bigint = BigInt(100);
注意:bigint类型需要ES2020或更高版本的编译目标。
数组类型
TypeScript中声明数组类型有以下两种方式。
// 方式一:类型后加[]
let arr1: number[] = [1, 2, 3];
let arr2: string[] = ["a", "b", "c"];
// 方式二:使用泛型Array<T>
let arr3: Array<number> = [1, 2, 3];
let arr4: Array<string> = ["a", "b", "c"];
两种写法效果完全相同,第一种写法更简洁,在实际开发中使用更为广泛,推荐使用。
元组类型
元组(Tuple)是TypeScript特有的类型,它表示一个已知元素数量和类型的数组,各元素的类型可以不同。
let tuple: [string, number] = ["hello", 42];
console.log(tuple[0]); // "hello"
console.log(tuple[1]); // 42
访问元组元素时,TypeScript会根据索引推断出正确的类型。
此外,元组还支持可选元素(Optional Elements)和剩余元素(Rest Elements)。可选元素表示在元组类型中,可以在某个元素类型后面加上?标记,表示该位置的元素是可选的,这意味着你可以只提供前面的必填元素,而省略后面的可选元素;剩余元素使用...类型[]语法,可以在元组末尾表示“任意数量的某种类型元素”。下面是两个例子。
// 可选元素
let tuple1: [string, number?] = ["hello"];
// 剩余元素
let tuple2: [string, ...number[]] = ["hello", 1, 2, 3];
枚举类型
枚举(Enum)用于定义一组命名的常量,这是TypeScript对JavaScript的重要扩展之一。
数字枚举
默认情况下,枚举成员的值从0开始自动递增。
enum Direction {
Up, // 0
Down, // 1
Left, // 2
Right // 3
}
let dir: Direction = Direction.Up;
console.log(dir); // 0
我们也可以手动指定成员的值。
enum Direction {
Up = 1,
Down, // 2
Left, // 3
Right // 4
}
字符串枚举
字符串枚举的每个成员都必须用字符串字面量初始化。
enum Direction {
Up = "UP",
Down = "DOWN",
Left = "LEFT",
Right = "RIGHT"
}
字符串枚举的优势在于运行时值更具可读性,便于调试。
常量枚举
使用const修饰的枚举在编译时会被内联,不会生成额外的代码。
const enum Direction {
Up,
Down,
Left,
Right
}
let dir = Direction.Up; // 编译后直接变成: let dir = 0;
常量枚举可以减少生成代码的体积,但无法在运行时通过枚举值反查枚举名。
any和unknown类型
any
any类型表示任意类型,使用any类型相当于放弃了类型检查。
let value: any = 42;
value = "hello";
value = true;
value.foo(); // 不会报错,但运行时可能出错
程序员圈子中讽刺某些人滥用any的一个笑话就是“把TypeScript写成了AnyScript”,虽然any类型提供了极大的灵活性,但过度使用会失去TypeScript类型系统的优势,因此除非万不得已应尽量避免使用。
unknown
unknown类型是TypeScript 3.0版本引入的类型安全版本的any。unknown类型的变量可以赋任意值,但在使用前必须进行类型检查或类型断言。
let value: unknown = 42;
value = "hello";
// 直接使用会报错
// console.log(value.length); // Error
// 需要类型检查后才能使用
if (typeof value === "string") {
console.log(value.length); // OK
}
// 或使用类型断言
console.log((value as string).length); // OK
在需要表示"任意类型"的场景下,推荐使用unknown而非any。
void和never类型
void
void类型表示没有任何类型,通常用于函数没有返回值的情况。
function log(message: string): void {
console.log(message);
}
除了用于声明函数返回值类型,声明void类型的变量没有太大意义,因为只能赋值undefined。
never
never类型表示永远不会出现的值的类型,这乍一听有点奇怪,实际上可能会用于类似以下场景。
// 函数抛出异常,永远不会正常返回
function throwError(message: string): never {
throw new Error(message);
}
// 函数包含无限循环,永远不会返回
function infiniteLoop(): never {
while (true) {}
}
never类型是所有类型的子类型,但没有任何类型是never的子类型(除了never本身)。
object类型
object类型表示非原始类型,即除了string、number、boolean、symbol、null、undefined之外的类型,这个类型较为少用。
let obj: object = { name: "Tom" };
obj = [1, 2, 3]; // OK,数组也是对象
obj = () => {}; // OK,函数也是对象
// obj = 42; // Error,number是原始类型
// obj = "hello"; // Error,string是原始类型
在实际开发中,我们更常用接口(interface)或类型别名(type)来定义对象的具体结构,这将在后续章节中详细介绍。
类型断言
类型断言用于告诉编译器“我知道这个值的类型”,编译器会根据断言进行类型检查,以发现潜在的错误。类型断言具体有两种写法。
let value: unknown = "hello";
// 方式一:尖括号语法
let len1: number = (<string>value).length;
// 方式二:as语法(推荐)
let len2: number = (value as string).length;
类型断言不会进行运行时的类型转换,它只是告诉编译器按照指定类型进行检查。
注意:在JSX中只能使用as语法,因为尖括号会与JSX标签产生歧义,因此推荐统一使用as语法。
字面量类型
TypeScript支持将具体的值作为类型使用,这称为字面量类型,下面是一些例子。
let direction: "up" | "down" | "left" | "right" = "up";
let count: 1 | 2 | 3 = 1;
let flag: true = true;
字面量类型通常与联合类型配合使用,可以精确地限制变量的取值范围,这在定义配置项、状态值等场景中非常有用。