模式切换
变量声明
TypeScript 变量的命名规则:
- 变量名称可以包含数字和字母
- 除了下划线 _ 和美元 $ 符号外,不能包含其他特殊字符,包括空格
- 变量名不能以数字开头
- 变量名区分大小写
声明方式
在 TypeScript 中,变量的声明与其他 JavaScript 风格的语言相似,但增加了类型注解的选项。以下是声明变量的几种方式:
typescript
var [变量名]: [类型] = 值;
var [变量名]: [类型];
var [变量名] = 值;
var [变量名];
使用 var 关键字(虽然在现代 TypeScript 代码中,let 和 const 更受推荐):
typescript
var isDone: boolean = false;
使用 let 关键字(有块级作用域):
typescript
let decimal: number = 6;
使用 const 关键字(用于声明常量,其值不能被重新分配):
typescript
const pi: number = 3.14159;
类型推断:如果你初始化变量时为其分配了一个值,TypeScript 可以根据该值推断变量的类型:
typescript
let isDone = false; // TypeScript 推断出 `isDone` 是 `boolean` 类型
let decimal = 6; // TypeScript 推断出 `decimal` 是 `number` 类型
声明变量的同时不赋值:在这种情况下,你需要明确指定变量的类型:
typescript
let myVar: string; // 声明一个字符串类型的变量,但没有赋值
myVar = "hello"; // 之后可以为它赋值
使用元组(表示一个已知元素数量和类型的数组):
typescript
let x: [string, number] = ["hello", 10];
枚举类型:
typescript
enum Color {Red, Green, Blue}
let c: Color = Color.Green;
任意类型:当你确实不知道变量的类型,或者它可能是多种类型之一时,你可以使用 any 类型:
typescript
let notSure: any = 4;
notSure = "maybe a string instead";
空值:变量可能有一个值,或者根本没有值。你可以使用 null 和 undefined 来表示这种情况。然而,默认情况下,null 和 undefined 是所有类型的有效值。如果你希望一个变量只能是 null 或 undefined,你可以使用 void 类型:
typescript
let u: undefined = undefined;
let n: null = null;
let voidVar: void; // 只能被赋值为 `undefined` 或 `null`(在严格的 null 检查模式下)
但在实践中,void 类型更常用于表示函数的返回类型,表示该函数不返回任何值。例如:
typescript
function warnUser(): void {
console.log("This is my warning message");
}
TypeScript 遵循强类型,如果将不同的类型赋值给变量会编译错误:
typescript
var num: number = "hello" // 这个代码会编译错误
解构赋值
在 TypeScript(以及 JavaScript)中,解构赋值是一种方便的方式来提取数组或对象的值,并将其赋值给单独的变量。这可以使代码更加简洁,易于阅读和理解。
数组解构赋值
对于数组,解构赋值允许你从一个数组中提取值,并将它们分配给单独的变量。
typescript
const [first, second] = [1, 2];
console.log(first); // 输出 1
console.log(second); // 输出 2
// 如果数组有更多的元素,它们将被忽略,除非使用剩余语法
const [firstElement, ...rest] = [1, 2, 3, 4];
console.log(firstElement); // 输出 1
console.log(rest); // 输出 [2, 3, 4]
对象解构赋值
对于对象,解构赋值允许你提取对象的属性,并将它们的值分配给变量。
typescript
const person = {
firstName: "Alice",
lastName: "Johnson"
};
// 解构对象属性
const {firstName, lastName} = person;
console.log(firstName); // 输出 "Alice"
console.log(lastName); // 输出 "Johnson"
// 你可以为解构的变量指定默认值
const {middleName = "Middle"} = person;
console.log(middleName); // 输出 "undefined",因为 person 对象中没有 middleName 属性
// 你也可以给解构的变量重命名
const {firstName: first, lastName: last} = person;
console.log(first); // 输出 "Alice"
console.log(last); // 输出 "Johnson"
嵌套解构
解构赋值也可以用于嵌套数组和对象。
typescript
// 嵌套数组解构
const [firstTwo, ...rest] = [[1, 2], [3, 4], [5, 6]];
console.log(firstTwo); // 输出 [1, 2]
console.log(rest); // 输出 [[3, 4], [5, 6]]
// 嵌套对象解构
const user = {
profile: {
firstName: "Bob",
lastName: "Smith"
}
};
const {profile: {firstName, lastName}} = user;
console.log(firstName); // 输出 "Bob"
console.log(lastName); // 输出 "Smith"
解构赋值与函数
解构赋值也常与函数参数一起使用,允许你更直接地处理传入的数组或对象。
typescript
function greet({firstName, lastName}: { firstName: string, lastName: string }) {
console.log(`Hello, ${firstName} ${lastName}!`);
}
greet({firstName: "Charlie", lastName: "Brown"}); // 输出 "Hello, Charlie Brown!"
解构赋值提供了更简洁、更直观的方式来处理复杂的数据结构,并且在 TypeScript 中,类型注解还可以帮助你确保数据的类型安全。
类型断言
类型断言(Type Assertion)可以用来手动指定一个值的类型。TypeScript 类型断言是一种方式,让你能够明确地告诉编译器:“我知道我正在做什么,这个值应该是这个类型”。这在你确定某个表达式的类型比编译器所推断的类型更具体时非常有用。
类型断言有两种形式:
- 尖括号语法 (
<Type>value
) - as 关键字 (
value as Type
)
尖括号语法
typescript
let someValue: any = "this is a string";
let strLength: number = (<string>someValue).length;
as 关键字语法
typescript
let someValue: any = "this is a string";
let strLength: number = (someValue as string).length;
两种语法是等价的,选择哪一种主要是基于个人或团队的编码风格。
为什么需要类型断言?
在 TypeScript 中,类型推断是非常强大的,但在某些情况下,编译器可能无法推断出你期望的类型,或者你可能比编译器更了解某个值的实际类型。在这些情况下,你可以使用类型断言来告诉编译器:“我知道这个值的确切类型”。
使用类型断言的注意事项:
- 类型断言应该谨慎使用,因为它会绕过 TypeScript 的类型检查系统。如果你错误地断言了一个值的类型,这可能会导致运行时错误。
- 在进行类型断言之前,确保你有足够的信心该值确实是你断言的类型。
- 类型断言不会改变运行时的实际类型,它只是在编译时告诉 TypeScript 编译器应该如何处理这个值。
假设你有一个函数,它返回一个 any 类型的值,但你知道这个值在某种条件下是字符串。尽管我们知道 value 在 if 语句中是字符串,但 TypeScript 编译器不能确定这一点,因此会报错。通过使用类型断言,我们可以告诉编译器我们确信 value 是字符串,并安全地访问其 length 属性:
typescript
function getValue(): any {
// ... some logic that might return a string or something else
}
let value = getValue();
if (typeof value === "string") {
console.log(value.length); // Error: Object is possibly 'undefined'.
}
// 使用类型断言来解决这个问题
if (typeof value === "string") {
console.log((value as string).length); // No error now
}
类型推断
TypeScript 的类型推断是一种强大的功能,它允许编译器根据代码的上下文自动确定变量的类型。这意味着你不需要显式地声明每个变量的类型,编译器会根据你如何使用这些变量来推断它们的类型。
下面是一个简单的例子,展示了 TypeScript 如何进行类型推断。在这个例子中,firstName、lastName、fullName、list、list2 和 person 的类型都是根据它们的赋值来推断的。对于函数 greet,我们传递了一个对象,该对象的类型也是根据传递的参数来推断的。
typescript
let isDone: boolean = false; // 显式声明类型为 boolean
let age: number = 25; // 显式声明类型为 number
let firstName: string = "Alice"; // 显式声明类型为 string
let lastName: string = "Smith";
let fullName: string = firstName + " " + lastName; // 类型推断为 string
let notSure: any = 4;
notSure = "maybe a string instead"; // any 类型可以接收任何类型的值
let list: number[] = [1, 2, 3]; // 显式声明为 number 数组
let list2: Array<number> = [1, 2, 3]; // 使用 Array<T> 语法
let x: { first: string, second: number } = {first: "hello", second: 10}; // 对象字面量推断类型
function greet(person: { firstName: string, lastName: string }) {
return "Hello, " + person.firstName + " " + person.lastName;
}
let person = {firstName: "John", lastName: "Doe"}; // 类型推断为 { firstName: string, lastName: string }
console.log(greet(person)); // 输出 "Hello, John Doe"
类型推断的规则
- 字面量类型推断:如果变量被赋值为一个字面量(如数字、字符串、布尔值、null、undefined 或对象字面量),那么变量的类型就被推断为字面量的类型。
- 数组类型推断:如果一个变量被赋值为一个数组字面量,那么变量的类型就被推断为相应的数组类型。
- 对象类型推断:如果一个变量被赋值为一个对象字面量,那么变量的类型就被推断为相应的对象类型。
- 函数参数和返回类型推断:在函数定义中,如果函数体内部使用了参数,并且参数的类型能够被推断出来,那么参数的类型就会被推断。同样地,如果函数的返回值类型能够被推断出来,那么返回值的类型也会被推断。
- 上下文类型推断:在某些情况下,TypeScript 会使用上下文信息来推断变量的类型。例如,在表达式
let x = y + 1;
中,如果 y 的类型是 number,那么 x 的类型也会被推断为 number。
类型推断的优点
- 简洁性:不需要显式声明每个变量的类型,代码更简洁。
- 灵活性:随着代码的变化,类型推断可以自动适应新的类型。
- 减少错误:类型推断有助于减少因类型不匹配而导致的错误。
注意事项
虽然类型推断非常有用,但在某些情况下,显式声明变量的类型可能更有帮助,特别是当类型不是很明显或者当你想给变量一个更具体的类型时。此外,类型推断可能不适用于复杂的类型或需要更精细控制的情况。在这些情况下,显式声明类型可以提供更好的代码可读性和维护性。
变量作用域
在 TypeScript 中,变量作用域指的是变量在代码中的可访问性。它决定了变量在哪里可以被读取和修改。TypeScript 支持两种基本的作用域:局部作用域(也称为块级作用域)和全局作用域。
此外,ES6 引入了 let 和 const 关键字,它们为 JavaScript(和因此也为 TypeScript)带来了块级作用域的新概念。
全局作用域
全局作用域中的变量在任何地方都是可见的。在 TypeScript 文件的顶层(不在任何函数或块内部)声明的变量具有全局作用域。
typescript
let globalVar = "I am global";
function myFunction() {
console.log(globalVar); // 可以访问全局变量
}
myFunction(); // 输出 "I am global"
局部作用域(块级作用域)
局部作用域中的变量仅在声明它们的函数或块内部可见。在 TypeScript 中,使用 let 和 const 声明的变量具有块级作用域。
typescript
function myFunction() {
let localVar = "I am local";
console.log(localVar); // 可以访问局部变量
}
myFunction(); // 输出 "I am local"
console.log(localVar); // 错误:localVar 在当前作用域中未定义
函数作用域
在 ES5 和更早版本的 JavaScript(以及 TypeScript)中,使用 var 关键字声明的变量具有函数作用域。这意味着这些变量在函数内部任何地方都是可见的,即使它们在函数内部的某个块中声明。
typescript
function myFunction() {
var funcVar = "I am functional";
if (true) {
var anotherVar = "I am also functional";
}
console.log(funcVar); // 可以访问
console.log(anotherVar); // 也可以访问,因为 `var` 有函数作用域
}
myFunction();
块级作用域(let 和 const)
使用 let 和 const 声明的变量具有块级作用域,这意味着它们只在声明它们的块(一对大括号 {})内部可见。
typescript
function myFunction() {
if (true) {
let blockVar = "I am block-scoped";
console.log(blockVar); // 可以访问
}
console.log(blockVar); // 错误:blockVar 在当前作用域中未定义
}
myFunction();
注意事项
- 使用 let 和 const 可以提供更好的控制变量的作用域,并有助于避免某些与 var 相关的问题,如变量提升(variable hoisting)和重复声明。
- const 声明的变量具有块级作用域,并且它们的值在声明后不能被重新分配(即它们是常量)。
- TypeScript 还支持类级作用域(也称为私有作用域),这是通过类的私有成员(使用 private 关键字)来实现的。