基础数据类型

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中所有的原始类型,包括stringnumberbooleannullundefinedsymbolbigint

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类型只有两个值:truefalse

let isDone: boolean = false;
let isValid: boolean = true;

null和undefined

nullundefined在TypeScript中既是值也是类型。默认情况下,它们是所有类型的子类型,但在严格模式(strictNullChecks开启)下,它们只能赋值给各自的类型或void类型。

let n: null = null;
let u: undefined = undefined;

在实际开发中,我们通常会开启严格模式,此时如果一个变量可能为nullundefined,需要使用联合类型来声明。

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版本引入的类型安全版本的anyunknown类型的变量可以赋任意值,但在使用前必须进行类型检查或类型断言。

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类型表示非原始类型,即除了stringnumberbooleansymbolnullundefined之外的类型,这个类型较为少用。

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;

字面量类型通常与联合类型配合使用,可以精确地限制变量的取值范围,这在定义配置项、状态值等场景中非常有用。

作者:Gacfox
版权声明:本网站为非盈利性质,文章如非特殊说明均为原创,版权遵循知识共享协议CC BY-NC-ND 4.0进行授权,转载必须署名,禁止用于商业目的或演绎修改后转载。