文章目录
TypeScript简介
什么是TypeScript?
简单地说TypeScript就是在JavaScript的基础上加上了类型检查。
TypeScript 与 JavaScript 有着不同寻常的关系。TypeScript 提供了 JavaScript 的所有功能,并在这些功能之上添加了一层: TypeScript 的类型系统。
例如,JavaScript 提供了诸如 string 和 number 这样的原始类型,但它不检查你在赋值时与类型是否匹配。TypeScript 提供了这样的功能。
这意味着你现有的运行良好的 JavaScript 代码也是 TypeScript 代码。TypeScript 的主要好处是,它可以检查代码中的意外行为,从而降低出现错误的机会。
(摘抄自:https://www.typescriptlang.org/zh/docs/handbook/typescript-in-5-minutes.html)
TypeScript安装
安装TypeScript需要用npm命令,而npm依赖于Node.js,因此首先得安装Node.js。
Node.js发布于2009年5月,实质是对Chrome V8引擎进行封装。可以在https://nodejs.org官网上下载最新的版本。
Node.js安装很简单,下载之后,按照向导一步一步根据默认配置操作即可,安装完成后,验证下是否安装成功。
在命令行工具中输入“node -v”和“npm -v”查看是否安装成功,如果安装成功就会显示版本号。
安装完npm之后,在命令行工具界面中输入命令“npm install -g typescript”全局安装TypeScript,稍等片刻,等待安装完成后,用命令tsc -v查看其版本号来验证是否安装成功。
第一行TypeScript代码
新建一个hi.ts文件,代码如下:
function hi(msg: string) : string {
return "hi " + msg
}
console.log(hi('days'))
用命令 tsc *.ts 编译TypeScript代码,会在同一个目录下生成一个js文件。
tsc hi.ts
编译后的 hi.js JavaScript代码
function hi(msg) {
return "hi " + msg;
}
console.log(hi('days'));
我们可以看到上述TypeScript必须给变量指定类型msg: string,而JavaScript代码的变量是不用指定类型的。
这是TypeScript作为一门静态语言和JavaScript作为一门动态语言最大的区别之一:
- 静态语言是在编译时确定变量的数据类型的语言,需要声明变量的类型,不能随意改变。(TypeScript)
- 动态语言是在运行时确定变量的数据类型的语言,不需要声明变量的类型,可以根据赋值改变。(JavaScript)
静态语言和动态语言都有各自的优点:
- 静态语言可以在编译阶段发现错误和优化代码,提高程序的效率和安全性。
- 动态语言可以在运行时动态改变程序的结构和功能,提高程序的灵活性和表达力。
静态语言可以在编译阶段发现错误和优化代码,这有什么好处呢?
我们来看一个JavaScript的例子:
if (1 < x < 3) {
// x是 *任何* 值都为真!
}
为什么这个表达式永远为真呢?
这是因为 JavaScript 的比较运算符是从左到右结合的,所以 if (1 < x < 3) 实际上相当于 if ((1 < x) < 3),先比较 1 和 x 的大小,然后再比较结果和 3 的大小。
而(1 < x)的结果只能是 true 或 false,它们在 JavaScript 中会被转换成数字 1 或 0,所以无论 x 是什么值,if (1 < x < 3) 都会转换成if ((0或者1) < 3),结果永远是 true。
如果想要判断 x 是否在 1 和 3 之间,正确的写法是 if (x > 1 && x < 3),使用逻辑与运算符 && 来连接两个条件。
TypeScript可以在代码运行之前就能发现这样的错误:
如果我们是在编写一个大型的项目,有成千上万行代码,这些奇怪的错误不及时发现,就会造成严重的后果。所以TypeScript就是为了适应大型项目的开发的。
TypeScript的主要特点
编译和擦除类型
TypeScript的类型仅仅是为了在编译的时候检查用的,一旦TypeScript的编译器完成了检查代码的工作,它就会擦除类型最终生成“已编译”的JavaScript代码。
类型注解并不属于 JavaScript(或者专业上所说的 ECMAScript)的内容,所以没有任何浏览器或者运行时能够直接执行不经处理的 TypeScript 代码。
这也是为什么 TypeScript 首先需要一个编译器,因为它需要经过编译,才能去除或者转换TypeScript独有的代码,从而让这些代码可以在浏览器上运行。
这意味着一旦您的代码被编译,生成的普通JavaScript代码便没有类型信息。
TypeScript代码最终是编译成JavaScript代码运行的。
这意味着,如果将代码从JavaScript迁移到TypeScript ,即使TypeScript认为代码有类型错误,也可以保证以相同的方式运行。
保持与JavaScript运行时行为相同是TypeScript的基本特性,你可以轻松地在两种语言之间转换,而不必担心一些细微差别可能会使程序抛出异常而停止工作。
类型推断
TypeScript 可以识别 JavaScript 语言,在许多情况下可以推断类型。
在创建变量并将其赋值给特定值时,TypeScript 可以感知其数据类型,例如 let helloWorld 赋值了字符串,那么 TypeScript 就可以推断出变量 helloWorld 是 string 类型。
定义类型
你可以使用 interface 关键字声明显式地描述此对象的内部数据的类型:
interface User {
name: string;
id: number;
}
然后你可以声明一个符合此接口(interface)的 JavaScript 对象,在变量声明后使用像 : TypeName 这样的语法:
interface User {
name: string;
id: number;
}
// ---分割线---
const user: User = {
name: "Hayes",
id: 0,
};
如果提供的数据类型与提供的接口不匹配,TypeScript 会警告:
name在接口中定义成了string类型,而在user对象中赋值为1,就会抛出类型不匹配的警告。
我们还可以定义一个带构造方法的类和接口:
interface User {
name: string;
id: number;
}
class UserAccount {
name: string;
id: number;
constructor(name: string, id: number) {
this.name = name;
this.id = id;
}
}
const user: User = new UserAccount("唐三", 1);
联合类型
联合类型使用管道符号(|)表示一个值可以是多种类型之一。这在函数参数和返回值中非常有用,可以让你处理具有不同类型的输入和输出。
例如:
type StringOrNumber = string | number;
function printValue(value: StringOrNumber) {
console.log(value);
}
printValue("hello"); // 输出 "hello"
printValue(42); // 输出 42
在这个例子中,我们创建了一个名为 StringOrNumber 的联合类型,表示值可以是 string 或 number 类型。我们的 printValue 函数接受一个 StringOrNumber 类型的参数,并将其打印到控制台。
泛型
TypeScript 的泛型是一种编程技术,允许你在定义函数、接口和类时使用类型参数,从而实现类型安全的代码重用。泛型在编译时保留类型信息,这有助于捕获类型错误,并提高代码的可维护性和可读性。
在 TypeScript 中,泛型使用尖括号(<>)来表示类型参数。例如,Array 表示一个元素类型为 T 的数组,其中 T 是类型参数。
泛型函数:
假设我们要创建一个函数,将一个数组中的所有元素都包装在一个对象中,如 {value: element}。我们可以使用泛型来实现这个函数,以便它适用于不同类型的数组:
function wrapInObject<T>(array: T[]) {
return array.map((element) => ({value: element}))
}
console.log(wrapInObject([1,2,3])) // 输出[ { value: 1 }, { value: 2 }, { value: 3 } ]
console.log(wrapInObject(['a','b','c'])) // 输出[ { value: 'a' }, { value: 'b' }, { value: 'c' } ]
这里,T 是一个类型变量,用于表示元素的类型。当我们实际调用 wrapInObject 时,TypeScript 会根据传入的数组类型推断出 T 的具体类型。
泛型类:
泛型同样可以应用于类。
例如,我们创建一个简单的栈类:
class Stack<T> {
private items: T[] = [];
push(item: T): void {
this.items.push(item);
}
pop(): T | undefined {
return this.items.pop();
}
}
const numberStack = new Stack<number>();
numberStack.push(1);
numberStack.push(2);
console.log(numberStack.pop()); // 输出:2
const stringStack = new Stack<string>();
stringStack.push("a");
stringStack.push("b");
console.log(stringStack.pop()); // 输出:"b"
在这个例子中,我们定义了一个包含一个类型参数 T 的泛型类 Stack。T 表示栈中元素的类型。当我们创建 Stack 的实例时,需要为 T 指定具体的类型。
接口
TypeScript 的接口(Interface)是一种定义对象结构和约束的方法。接口可以描述对象的形状(即属性和方法),并为类和函数定义约定。通过接口,我们可以确保代码符合预期的结构和行为,提高代码的可读性和可维护性。
接口主要有以下几个用途:
描述对象的形状:
接口可以用来描述对象应该具有哪些属性和方法。这使得我们可以在编写代码时获得类型检查和智能提示,从而减少出错的可能性。
例如,我们可以定义一个描述用户信息的接口:
interface User {
id: number;
name: string;
age: number;
}
function printUserInfo(user: User): void {
console.log(`ID: ${user.id}, Name: ${user.name}, Age: ${user.age}`);
}
const user: User = { id: 1, name: "Alice", age: 30 };
printUserInfo(user);
在这个例子中,User 接口定义了用户信息应具有的属性。printUserInfo 函数接受一个 User 类型的参数,这样我们可以确保传递给该函数的对象具有正确的结构。
描述函数类型:
接口也可以用来描述函数的类型。这包括函数的参数类型和返回值类型。
例如,我们可以定义一个用于比较两个值的接口:
interface Comparator<T> {
(a: T, b: T): number;
}
const numberComparator: Comparator<number> = (a, b) => {
return a - b;
};
const stringLengthComparator: Comparator<string> = (a, b) => {
return a.length - b.length;
};
在这个例子中,Comparator 接口定义了一个泛型函数类型,用于比较两个值。我们创建了两个不同类型的比较器:一个用于比较数字,另一个用于比较字符串的长度。
实现接口的类:
类可以实现接口,以确保遵循特定的约定。当一个类实现了一个接口时,TypeScript 会检查该类是否具有接口所要求的属性和方法。
例如,我们可以定义一个表示几何形状的接口和一个实现该接口的类:
interface Shape {
area(): number;
perimeter(): number;
}
class Rectangle implements Shape {
constructor(private width: number, private height: number) {}
area(): number {
return this.width * this.height;
}
perimeter(): number {
return 2 * (this.width + this.height);
}
}
const rectangle = new Rectangle(10, 5);
console.log(rectangle.area()); // 输出:50
console.log(rectangle.perimeter()); // 输出:30
在这个例子中,Shape 接口定义了几何形状应具有的方法。Rectangle 类实现了 Shape 接口,确保了它具有正确的方法。
通过使用接口,我们可以确保对象、函数和类具有预期的结构和行为。
运行 .ts 文件的方法
方法一:先将 .ts 文件编译成 .js,再运行 .js 文件
- 安装 typescript
npm install -g typescript // 如果已经安装过请忽略这步
- 将 .ts 文件编译成 .js 文件,例如文件名为test.ts
tsc test.ts // 会在当前目录下生成对应的 test.js 文件
- 运行 .js 文件
node test.js
方法二:通过 ts-node 直接运行 .ts 文件
- 安装 typescript、ts-node
npm install -g typescript ts-node
- 直接运行 .ts 文件
ts-node test.ts
方法三:在线运行
在浏览器打开:https://www.typescriptlang.org/play
可以在线运行:
总结
TypeScript 是 JavaScript 的一个超集,它在 JavaScript 的基础上引入了一些额外的特性,旨在帮助开发者编写更健壮、易于维护的代码。下面是 TypeScript 的一些主要特点:
- 静态类型检查:TypeScript 提供了静态类型检查,这意味着在代码运行之前,TypeScript 就能检查代码中的类型错误。这有助于及时发现和修复潜在的问题,降低运行时出现错误的风险。
- 类型推断:TypeScript 能够根据变量的使用和赋值来推断其类型。这样,在许多情况下,你无需显式地为变量指定类型,TypeScript 会自动为你推断出正确的类型。
- 泛型:TypeScript 支持泛型编程,允许你创建可以适用于多种类型的函数、类和接口。泛型有助于提高代码的复用性,同时保持类型安全。
- 接口:TypeScript 的接口可以用来描述对象的形状(即属性和方法),以及定义类和函数的约定。接口可以帮助确保代码符合预期的结构和行为。
- 类和面向对象编程:TypeScript 提供了对类和面向对象编程特性的完整支持,包括继承、封装、抽象类和访问修饰符等。这有助于组织和管理大型代码库。
- 装饰器:TypeScript 支持装饰器,这是一种元编程特性,允许你修改或扩展类、属性、方法和参数的行为。装饰器有助于编写可重用和可组合的代码
- 丰富的工具支持:TypeScript 可以与许多流行的代码编辑器(如 Visual Studio Code)集成,提供智能感知、自动补全、重构等功能。这有助于提高开发者的工作效率。
- 良好的生态系统:TypeScript 可以与许多现代前端框架(如 Angular、React 和 Vue.js)无缝集成,并拥有大量的类型定义文件,使得你可以轻松地为第三方库添加类型支持。
总之,TypeScript 的特点包括静态类型检查、类型推断、泛型、接口、面向对象编程支持、装饰器以及丰富的工具和生态系统支持。这些特性使得 TypeScript 成为开发大型、可维护的 JavaScript 项目的理想选择。
参考资料:https://www.typescriptlang.org/zh/docs/handbook/typescript-from-scratch.html