TypeScript 基础类型
1. 原始数据类型
JavaScript 的原始数据类型包括:boolean、number、string、null、undefined、symbol 和 bigint。TypeScript 为这些类型都提供了相应的类型注解。
布尔值 (boolean)
使用 boolean 定义布尔值类型。
let isDone: boolean = false;
let isVisible: boolean = true;
注意:
boolean是原始类型,而Boolean是一个包装对象。应始终使用boolean。let createdByNewBoolean: boolean = new Boolean(1); // Error: Type 'Boolean' is not assignable to type 'boolean'.
数值 (number)
使用 number 定义所有数字类型,包括整数、浮点数,以及 ES6+ 的二进制、八进制、十六进制字面量。
let decimal: number = 6;
let hex: number = 0xf00d;
let binary: number = 0b1010;
let octal: number = 0o744;
let notANumber: number = NaN;
let infinityNumber: number = Infinity;
字符串 (string)
使用 string 定义字符串类型。推荐使用模板字符串来拼接多行文本或嵌入变量。
let myName: string = "Alice";
let myAge: number = 30;
// 模板字符串
let sentence: string = `Hello, my name is ${myName}.
I'll be ${myAge + 1} years old next year.`;
Null 和 Undefined
null 和 undefined 各自拥有自己的类型,名为 null 和 undefined。
let u: undefined = undefined;
let n: null = null;
默认情况下,null 和 undefined 是所有类型的子类型,可以把它们赋值给 number 等类型的变量。但在实际开发中,这通常是错误的根源。强烈推荐开启 strictNullChecks 编译选项。开启后,null 和 undefined 只能赋值给 void、any、unknown 和它们各自的类型。这能有效避免大量的潜在运行时错误。
// tsconfig.json --> "strictNullChecks": true
let num: number = undefined; // Error: Type 'undefined' is not assignable to type 'number'.
let str: string = null; // Error: Type 'null' is not assignable to type 'string'.
// 只在需要时,通过联合类型显式允许 null 或 undefined
let nullableString: string | null = "hello";
nullableString = null; // OK
void
void 用于表示没有任何返回值的函数。声明一个 void 类型的变量没有实际用途。
function logMessage(message: string): void {
console.log(message);
// 没有 return 语句
}
Symbol
symbol 是 ES6 引入的一种原始数据类型,它的主要特点是唯一且不可变。每次调用 Symbol() 都会创建一个全新的、独一无二的值。
const sym1 = Symbol("key");
const sym2 = Symbol("key");
console.log(sym1 === sym2); // false,即使描述相同,值也不同
symbol 最核心的用途是创建唯一的对象属性键,以避免属性名冲突。这在多人协作或扩展第三方库对象时尤其有用。
const userID = Symbol("id");
let user = {
name: "Alice",
[userID]: "12345", // 使用 symbol 作为属性键
};
console.log(user.name); // 'Alice'
console.log(user[userID]); // '12345'
// Symbol 属性默认是不可枚举的
console.log(Object.keys(user)); // ['name']
console.log(Object.getOwnPropertySymbols(user)); // [Symbol(id)]
for (const key in user) {
console.log(key); // 只会打印 'name'
}
BigInt
bigint 是 ES2020 引入的一种数值类型,用于表示超出 number 类型安全整数范围(Number.MAX_SAFE_INTEGER)的极大整数。它通过在整数字面量后添加 n 来定义。
const big: bigint = 100n;
const alsoBig: bigint = BigInt("9007199254740991");
// 注意:bigint 和 number 不是同一类型,不能混合运算
// const mixed = big + 10; // Error: Operator '+' cannot be applied to types 'bigint' and 'number'.
2. Any 类型
any 类型是 TypeScript 的一个“后门”,它允许我们绕过编译时的类型检查。
一个被标记为 any 的变量可以被赋予任何类型的值。
let myFavoriteNumber: any = "seven";
myFavoriteNumber = 7; // OK
对 any 类型的变量进行任何操作(访问属性、调用方法)都是允许的,并且返回的值也是 any 类型。这被称为 any 的“传染性”,它会污染代码库的类型安全。
let anyThing: any = "hello";
console.log(anyThing.nonExistentProperty); // 不会报错
anyThing.someMethod(); // 不会报错
let result = anyThing.foo(); // result 的类型也是 any
unknown 类型是 TypeScript 3.0 引入的,作为 any 的类型安全对应物。任何值都可以赋给 unknown,但 unknown 类型的值不能赋给除了 any 和 unknown 之外的任何类型。在使用 unknown 类型的值之前,必须进行类型收窄(如类型检查或断言)。
let notSure: unknown = 4;
notSure = "maybe a string instead";
let aNumber: number;
// aNumber = notSure; // Error: Type 'unknown' is not assignable to type 'number'.
// 必须先进行类型检查
if (typeof notSure === "number") {
aNumber = notSure; // OK
}
最佳实践: 只有在万不得已时才使用
any。在大多数情况下,unknown是一个更安全的选择。
3. 联合类型 (Union Types)
联合类型表示一个值可以是几种类型之一,使用 | 分隔。
let value: string | number;
value = "hello"; // OK
value = 123; // OK
// value = false; // Error
当不确定联合类型变量的具体类型时,只能访问所有类型共有的属性或方法。为了使用特定类型的属性或方法,需要进行类型收窄。
function getLength(something: string | number): number {
// return something.length; // Error: Property 'length' does not exist on type 'number'.
// 使用 typeof 进行类型收窄
if (typeof something === "string") {
return something.length; // OK, something 在这里被认为是 string
} else {
return something.toString().length; // OK, something 在这里被认为是 number
}
}
4. 数组与元组
数组 (Array)
有多种方式定义数组:
- 类型 + 方括号:
let list: number[] = [1, 2, 3];(最常用) - 数组泛型:
let list: Array<number> = [1, 2, 3]; - 接口:
interface NumberArray { [index: number]: number; }(不常用)
数组中的元素必须与定义的类型一致。
元组 (Tuple)
元组允许我们定义一个已知元素数量和类型的数组,各元素的类型不必相同。
// 定义一个包含 string 和 number 的元组
let x: [string, number];
x = ["hello", 10]; // OK
// x = [10, 'hello']; // Error
console.log(x[0].substring(1)); // OK
// console.log(x[1].substring(1)); // Error: Property 'substring' does not exist on type 'number'.
5. 函数类型
函数定义
为函数添加类型注解,需要同时为参数和返回值指定类型。
// 函数声明
function add(x: number, y: number): number {
return x + y;
}
// 函数表达式
const subtract: (base: number, decrementor: number) => number = function (
x,
y,
) {
return x - y;
};
在函数表达式中,(base: number, decrementor: number) => number 就是完整的函数类型定义。
可选、默认和剩余参数
- 可选参数: 使用
?标记,必须位于必需参数之后。 - 默认参数: 为参数提供默认值,该参数会被视为可选。
- 剩余参数: 使用
...语法将多个参数收集到一个数组中。
// lastName 是可选参数
function buildName(firstName: string, lastName?: string) {
return lastName ? `${firstName} ${lastName}` : firstName;
}
// a 和 b 都有默认值
function calculate(a: number = 10, b: number = a / 2) {
return a + b;
}
// ...items 是剩余参数,它是一个数组
function sum(...items: number[]): number {
return items.reduce((total, current) => total + current, 0);
}
重载
重载允许一个函数根据不同的参数类型或数量返回不同的结果类型。它通过提供多个函数签名和一个通用的实现体来工作。
// 重载签名
function reverse(x: number): number;
function reverse(x: string): string;
// 实现签名(对外部不可见)
function reverse(x: number | string): number | string {
if (typeof x === "number") {
return Number(x.toString().split("").reverse().join(""));
} else {
return x.split("").reverse().join("");
}
}
const numResult = reverse(123); // numResult 的类型是 number
const strResult = reverse("abc"); // strResult 的类型是 string