了解 TypeScript 是什么
背景
TypeScript 是由 Microsoft 开发和维护的一个开源编程语言。它是 JavaScript 的一个超集,意味着任何有效的 JavaScript 代码也是有效的 TypeScript 代码。TypeScript 在 JavaScript 的基础上增加了类型系统和对 ES6+ 新特性的支持,目的是帮助开发者在开发大型应用时提高代码的可靠性和开发效率。
优势
- 类型系统:TypeScript 的最大优势是其静态类型系统,它可以在编译时捕获常见的错误,提高开发效率和代码质量。
- ES6+ 支持:TypeScript 支持最新的 JavaScript 特性,包括但不限于类、模块、箭头函数等。
- 工具支持:得益于类型系统,TypeScript 在代码编辑器和 IDE 中提供了更好的自动完成、导航和重构功能。
- 社区与生态:TypeScript 有着强大的社区支持和丰富的类型定义库(DefinitelyTyped),使得在 TypeScript 中使用第三方 JavaScript 库变得简单。
如何在 JavaScript 的基础上添加了类型系统
TypeScript 通过为变量、函数参数和返回值等添加类型注解,引入了静态类型检查。这些类型注解是可选的,使得开发者可以逐渐迁移到 TypeScript 上。
安装和配置
安装 TypeScript 编译器
可以通过 npm(Node.js 的包管理器)安装 TypeScript 编译器:
npm install -g typescript
这条命令会全局安装 TypeScript 编译器,让我们能在任何地方通过命令行使用 tsc
命令。
配置 tsconfig.json 文件
tsconfig.json
文件用于指定编译器如何编译 TypeScript 代码。创建此文件可以通过运行 tsc --init
命令。在这个文件中,可以配置诸如编译目标版本、模块系统、源映射支持等选项。
基本类型
TypeScript 支持 JavaScript 的所有基本类型,同时引入了一些新的类型,以帮助开发者更精确地描述变量的类型:
- string:文本数据类型,如
"hello, TypeScript"
。 - number:数字类型,包括整数和浮点数,如
42
或3.14
。 - boolean:布尔值类型,
true
或false
。 - array:数组类型,可以是
number[]
或Array<number>
。 - tuple:元组类型,允许表示一个已知元素数量和类型的数组,每个元素的类型不必相同,如
[string, number]
。 - enum:枚举类型,用于定义一组命名常量。
- any:任意类型,可以是任何类型的值。尽量避免使用,以充分利用 TypeScript 的类型系统。
- void:表示没有任何类型,常用于没有返回值的函数。
- null 和 undefined:与 JavaScript 中的
null
和undefined
值相对应。
变量声明
TypeScript 支持使用 let
和 const
进行变量声明,这两种声明方式都提供了块级作用域:
- let:用于声明一个可变的变量。
- const:用于声明一个常量,声明时必须初始化,且不能再被重新赋值。
在 TypeScript 中,建议尽量使用 const
来声明变量,除非确定变量的值会改变,这样做可以帮助避免编程中的一些错误。
类型系统深入
接口
在 TypeScript 中,接口(Interfaces)用于定义对象的形状,即指定对象应有哪些属性以及属性的类型。接口是定义代码约束和契约的强大方式,而不是为了实现它们。
interface Person {
name: string;
age: number;
}
function greet(person: Person) {
console.log("Hello, " + person.name);
}
类
TypeScript 的类(Classes)语法是基于 ES6 的类语法,并添加了一些新的特性,如类型注解、接口实现、静态属性等。类可以实现接口,用于定义一个具有某些特定属性和方法的对象。
interface Person {
name: string;
greet(): void;
}
class Employee implements Person {
constructor(public name: string, public position: string) {}
greet() {
console.log(`Hello, my name is ${this.name} and I work as a ${this.position}.`);
}
}
类也支持继承,允许创建一个子类,继承父类的属性和方法,并添加新的或修改现有的属性和方法。
class Manager extends Employee {
constructor(name: string, position: string, public department: string) {
super(name, position);
}
greet() {
console.log(`Hello, I'm ${this.name}, the manager of the ${this.department} department.`);
}
}
函数
TypeScript 允许为函数的参数和返回值指定类型。此外,还可以定义可选参数、默认参数、剩余参数等。
function buildName(firstName: string, lastName?: string): string {
if (lastName) return `${firstName} ${lastName}`;
else return firstName;
}
function buildAddress(...streets: string[]): string {
return streets.join(", ");
}
泛型
泛型(Generics)是 TypeScript 的一个重要特性,允许创建可在多个类型之间工作的组件。它们用于创建高度可重用的组件,能够支持任何类型。
function identity<T>(arg: T): T {
return arg;
}
let output1 = identity<string>("myString");
let output2 = identity<number>(100);
TypeScript 的逻辑控制语法
TypeScript 的逻辑控制语法与 JavaScript 非常相似,因为 TypeScript 是 JavaScript 的超集。这包括条件语句、循环语句等。
- 条件语句(如
if
、else
、switch
)用于基于不同条件执行不同的代码分支。
if (condition) {
// do something when condition is true
} else {
// do something when condition is false
}
- 循环语句(如
for
、while
、do...while
)用于重复执行一段代码。
for (let i = 0; i < 5; i++) {
console.log(i);
}
let i = 0;
while (i < 5) {
console.log(i);
i++;
}
- for…of 循环用于遍历可迭代对象(如数组、字符串)的元素。
let array = [10, 20, 30];
for (let value of array) {
console.log(value); // 10, 20, 30
}
- for…in 循环用于遍历对象的键。
let obj = {a: 1, b: 2, c: 3};
for (let key in obj) {
console.log(key); // 'a', 'b', 'c'
}
使用这些逻辑控制结构时,TypeScript 提供的类型系统可以帮助确保变量的类型安全,例如,在条件检查中使用类型守卫来细化类型。
高级主题
高级类型
- 联合类型:允许一个值是几种类型之一,使用竖线(
|
)分隔每个类型。这对于函数参数或变量可以接受不同类型的值时非常有用。
function printId(id: number | string) {
console.log("Your ID is: " + id);
}
- 交叉类型:允许将多个类型合并为一个类型,使用
&
符号。这对于需要将多个类型的属性组合到一起的对象非常有用。
interface Name {
name: string;
}
interface Age {
age: number;
}
type Person = Name & Age;
let p: Person = { name: "Alice", age: 25 };
- 类型守卫:是执行运行时检查的一种方式,可以确保在某个作用域内变量属于某个特定的类型。
function isNumber(x: any): x is number {
return typeof x === "number";
}
function isString(x: any): x is string {
return typeof x === "string";
}
- 类型断言:允许手动指定一个值的类型,使用
as
关键字或尖括号语法。
let someValue: any = "this is a string";
let strLength: number = (someValue as string).length;
装饰器
装饰器是一种特殊类型的声明,它能够被附加到类声明、方法、访问符、属性或参数上。装饰器使用 @expression
形式,expression
求值后必须为一个函数,它会在运行时被调用。
function sealed(constructor: Function) {
Object.seal(constructor);
Object.seal(constructor.prototype);
}
@sealed
class Greeter {
greeting: string;
constructor(message: string) {
this.greeting = message;
}
greet() {
return "Hello, " + this.greeting;
}
}
命名空间和模块
- 命名空间:是 TypeScript 的一个早期方式,用于组织代码和避免全局作用域污染。虽然在使用模块的项目中不常用,但在某些情况下仍然有用。
namespace Validation {
export interface StringValidator {
isAcceptable(s: string): boolean;
}
}
- 模块:TypeScript 模块的概念与 ES6 模块相似,用于组织和封装代码。每个模块可以导出或导入类、接口、函数、对象等。
// someModule.ts
export function someFunction() { /*...*/ }
// otherModule.ts
import { someFunction } from "./someModule";
映射类型
映射类型允许从一个旧的类型生成一个新的类型,其中的属性是基于旧类型的属性进行转换的。
type Readonly<T> = {
readonly [P in keyof T]: T[P];
};
type Partial<T> = {
[P in keyof T]?: T[P];
};
interface Person {
name: string;
age: number;
}
type ReadonlyPerson = Readonly<Person>;
type PartialPerson = Partial<Person>;