教练,我想练TypeScript类型体操!!!
前言
大家好,我是倔强青铜三。是一名热情的软件工程师,我热衷于分享和传播IT技术,致力于通过我的知识和技能推动技术交流与创新,欢迎关注我,微信公众号:倔强青铜三。 欢迎点赞
、收藏
、关注
,一键三连!!!
TypeScript已经成为构建大型应用的行业标准,众多组织选择它作为主要的应用开发语言。
本教程将作为你学习TypeScript的入门指南。它旨在满足从初学者到高级用户的各个阶段的学习者需求,教授基础和高级的TypeScript概念,是任何想要深入了解TypeScript的人的有用资源。
本指南的目标不是成为穷尽资源,而是成为一个简洁且实用的参考。它将TypeScript的精髓提炼成易于消化的格式。
无论你是刚开始的新手,想要巩固知识的中级学习者,还是需要快速复习的高级用户,本指南都旨在满足你的TypeScript学习需求。
通过仔细阅读本教程并实践其中的例子,你应该能够构建健壮、可扩展且可维护的TypeScript应用程序。我们将涵盖关键的TypeScript概念,如类型、函数、类、接口、泛型等。
先决条件
在开始本指南之前,你应该具备基本的JavaScript知识。熟悉面向对象编程概念,如类、接口和继承也是推荐的。
但如果你对这些概念还不熟悉,不用担心——我们将在本指南中详细覆盖它们。
本指南适合谁?
本指南适合任何想要学习TypeScript的人。无论你是初学者、中级学习者还是高级用户,本指南旨在满足你的TypeScript学习需求。
它也是想要重温TypeScript技能的人的实用参考。
TypeScript与JavaScript
TypeScript是JavaScript的静态类型超集,旨在增强大规模应用的开发。
它引入了可选的静态类型
、类
和接口
到JavaScript
中,与C#
和Java
等语言相似。TypeScript代码被转译成纯JavaScript,确保在各种JavaScript环境中的兼容性。
虽然TypeScript和JavaScript可以在相同的环境中运行,但它们展现出关键的不同。主要的区别在于TypeScript是静态类型的,提供了类型安全性,而JavaScript是动态类型的。
让我们通过一个例子来探索JavaScript和TypeScript之间的差异:
// JavaScript
function add(a, b) {
return a + b;
}
// 调用函数
add(1, 2); // 返回:3
在上面的JavaScript示例中,add
函数接收两个参数a
和b
,并返回它们的和。函数用参数1
和2
调用,得到3
。请注意,函数参数没有用类型注释,这是JavaScript中的典型做法。
现在,让我们看看如何用TypeScript编写相同的函数:
// TypeScript
function add(a: number, b: number): number {
return a + b;
}
// 调用函数
add(1, 2); // 返回:3
在TypeScript版本中,我们用number
类型注释了参数a
和b
。我们还指定了函数返回一个number
。
这是JavaScript和TypeScript之间的一个关键区别。TypeScript强制执行类型安全,意味着它在编译时检查值的类型,并在它们与预期类型不匹配时抛出错误。
这个特性有助于在开发过程中早期捕获错误,使TypeScript成为大型应用的流行选择。
TypeScript和JavaScript都是用于各种应用的强大语言。让我们总结它们的关键区别:
特性 | TypeScript | JavaScript |
---|---|---|
类型系统 | 静态类型(编译时检查类型) | 动态类型(运行时检查类型) |
超集 | 是,TypeScript是JavaScript的超集 | 无 |
编译 | 需要编译(或转译)成JavaScript | 不需要编译 |
面向对象特性 | 包括接口、泛型和装饰器等高级面向对象特性 | 通过原型支持面向对象,不原生支持接口或泛型 |
工具 | 提供更好的工具,如自动补全、类型检查和源图支持 | 基本工具 |
社区和生态系统 | 新兴的,较小的社区和生态系统 | 自1995年以来,拥有庞大的社区和庞大的库和框架生态系统 |
学习曲线 | 由于额外的特性而更陡峭 | 通常对初学者更容易 |
用例 | 通常用于大型代码库,类型检查和自动补全的优势最为明显 | 用于客户端和服务器端开发,可以在浏览器中原生执行 |
TypeScript的优势
TypeScript提供了几个优于JavaScript的优势:
- 改进的工具:TypeScript的静态类型启用了更好的工具支持。像
自动补全
、类型推断
和类型检查这样的功能使得开发过程更高效、更愉快。 - 更好的文档:TypeScript代码库通常更容易理解和维护。类型注释作为隐式文档,使得理解函数期望和返回的值类型更容易。
- 高级特性:TypeScript支持像
装饰器
和async/await
这样的高级JavaScript特性,它还引入了JavaScript中不可用的特性,如接口
、枚举
和元组
。 - 重构:TypeScript的工具使得重构更大的代码库更安全、更可预测。你可以自信地进行大规模更改。
- 逐步采用:TypeScript是JavaScript的超集,这意味着你可以逐步在项目中采用TypeScript。你可以从将
.js
文件重命名为.ts
开始,然后你可以逐步添加类型注释。 - 社区和生态系统:TypeScript拥有不断增长的社区和生态系统。许多流行的JavaScript库,如React和Angular,都有TypeScript定义,这使得在TypeScript项目中使用它们更容易。
代码生成
TypeScript代码不是浏览器或Node.js原生理解的,所以它需要被转译成JavaScript。这个转译过程由TypeScript编译器(tsc
)处理,它读取TypeScript代码并生成等效的JavaScript代码。
要转译一个TypeScript文件,你可以使用tsc
命令后跟文件名:
$ tsc index.ts
此命令将index.ts
文件转译成一个相同目录下的index.js
文件。生成的JavaScript代码可以在任何JavaScript环境中执行,比如浏览器或Node.js。
监听文件变化
在积极开发过程中,让你的TypeScript代码在你进行更改时自动重新编译是有益的。TypeScript编译器提供了--watch
选项用于此目的:
$ tsc index.ts --watch
使用此命令,编译器将监视index.ts
文件,并在检测到更改时自动重新编译它。
配置TypeScript编译器
对于更大的项目,通常有一个配置文件tsconfig.json
来管理编译器选项。此文件允许你指定根级文件和编译项目所需的编译器选项。
以下是tsconfig.json
文件的一个示例:
{
"compilerOptions": {
"target": "es5",
"module": "commonjs",
"strict": true,
"outDir": "./dist"
},
"include": ["src/**/*.ts"],
"exclude": ["node_modules"]
}
在此配置中,compilerOptions
对象包含编译器的选项。target
选项指定ECMAScript目标版本,module
选项设置模块系统,strict
选项启用广泛的类型检查行为,outDir
选项指定编译的JavaScript文件的输出目录。
include
和exclude
选项用于指定要编译和忽略的文件。
要基于tsconfig.json
文件编译项目,你可以不带任何参数运行tsc
命令:
$ tsc
此命令将根据tsconfig.json
文件中指定的选项编译项目中的所有TypeScript文件。
TypeScript基础
在本节中,我们将介绍TypeScript的基础知识。你将看到更多TypeScript静态类型的示例,并了解其工具和错误检查。
安装
在我们深入TypeScript之前,你需要确保你的系统上安装了Node.js。Node.js是一个运行环境,允许你在浏览器之外运行JavaScript。你可以从官方网站下载Node.js。
Node.js安装完成后,你可以使用Node.js附带的Node包管理器(npm)安装TypeScript。
打开终端并运行以下命令:
npm install -g typescript
此命令在你的系统上全局安装TypeScript。你可以通过运行tsc
命令来确认安装,tsc
代表TypeScript编译器:
tsc --version
此命令应该显示你安装的TypeScript版本。
现在TypeScript安装好了,我们准备好开始TypeScript之旅了!
配置
太好了!现在我们已经安装了TypeScript,让我们讨论另一个重要事项:配置
。对于更大的项目,通常有一个配置文件tsconfig.json
来管理编译器选项。此文件允许你指定根级文件和编译项目所需的编译器选项。
. 剩余参数:TypeScript支持剩余参数,允许你将任意数量的参数传递给函数。
示例:
function sum(...numbers: number[]): number {
return numbers.reduce((a, b) => a + b, 0);
}
let total: number = sum(1, 2, 3, 4, 5); // total是15
在这个示例中,sum
函数接受任意数量的参数,并返回它们的和。
TypeScript中的类和对象
类是面向对象编程(OOP)的基本部分。它们是创建对象的模板,提供状态(成员变量)的初始值和行为(成员函数或方法)的实现。
TypeScript支持类,使用class
关键字声明。TypeScript类的一个优势是它们支持面向对象编程(OOP)特性,如继承
、封装
和多态
。
- 类声明:在TypeScript中,类使用
class
关键字声明。
示例:
class Person {
name: string;
age: number;
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
greet(): void {
console.log(`Hello, my name is ${this.name} and I am ${this.age} years old.`);
}
}
let john = new Person('John', 25);
john.greet(); // 输出:Hello, my name is John and I am 25 years old.
在这个示例中,Person
是一个具有两个属性name
和age
以及一个方法greet
的类。constructor
是一个特殊的方法,用于创建和初始化使用类创建的对象。
- 继承:TypeScript支持继承,一种基于另一个类创建类的机制,保留相似的实现。继承使用
extends
关键字实现。
示例:
class Employee extends Person {
department: string;
constructor(name: string, age: number, department: string) {
super(name, age);
this.department = department;
}
greet(): void {
super.greet();
console.log(`I work in ${this.department}.`);
}
}
let jane = new Employee('Jane', 30, 'HR');
jane.greet(); // 输出:Hello, my name is Jane and I am 30 years old. I work in HR.
在这个示例中,Employee
是一个扩展了Person
的类。它添加了一个新的属性department
并覆盖了greet
方法。super
关键字用于调用父类的对应方法。
- 抽象类:抽象类是不能被直接实例化的类。它们被用作其他类的基类,并可能包含必须由派生类实现的抽象方法。
示例:
abstract class Shape {
abstract area(): number;
}
class Circle extends Shape {
radius: number;
constructor(radius: number) {
super();
this.radius = radius;
}
area(): number {
return Math.PI * this.radius ** 2;
}
}
let circle = new Circle(5);
console.log(circle.area()); // 输出:78.54
在这个示例中,Shape
是一个具有抽象方法area
的抽象类。Circle
类扩展了Shape
并实现了area
方法。抽象类适用于定义一组类的共同接口。
- 封装:封装是将数据(属性)和操作数据(方法)的方法捆绑到一个称为类的单个单元中。在TypeScript中,封装通过使用
public
、private
和protected
等访问修饰符实现。
示例:
class Person {
private name: string;
protected age: number;
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
greet(): void {
console.log(`Hello, my name is ${this.name} and I am ${this.age} years old.`);
}
}
let john = new Person("John", 25);
console.log(john.name); // 错误:属性"name"是私有的
console.log(john.age); // 错误:属性"age"是受保护的
在这个示例中,name
是Person
类的私有属性,所以它不能从类外部被访问。age
是受保护的属性,所以它可以从子类中被访问,但不能从类外部被访问。
- 多态:多态是一个对象可以采取多种形式的能力。在TypeScript中,多态通过方法覆盖实现,子类中的方法与超类中的同名方法具有相同的名称和签名。
示例:
class Animal {
speak(): void {
console.log('Animal makes a sound');
}
}
class Dog extends Animal {
speak(): void {
console.log('Dog barks');
}
}
let animal: Animal = new Dog();
animal.speak(); // 输出:Dog barks
在这个示例中,Animal
是一个具有speak
方法的基类。Dog
是覆盖了speak
方法的子类。当将Dog
的实例赋给Animal
类型的变量时,会调用Dog
的speak
方法。
- 访问修饰符:TypeScript支持
public
、private
和protected
等访问修饰符。默认情况下,每个成员都是public
。
示例:
class Animal {
private name: string;
constructor(name: string) {
this.name = name;
}
public getName(): string {
return this.name;
}
}
let dog = new Animal('Dog');
console.log(dog.getName()); // 输出:Dog
在这个示例中,name
是Animal
类的私有成员。它只能在Animal
类内部被访问。getName
方法是公共的,所以它可以从类外部被调用。
TypeScript中的接口
接口在TypeScript中是定义代码中合同的强大方式。它们用于类型检查,确保对象符合特定的结构。
通过定义接口,我们可以命名一个特定的变量组合,确保它们始终被用作一组。
- 接口声明:接口使用
interface
关键字声明。
示例:
interface Person {
name: string;
age: number;
}
let john: Person = { name: 'John', age: 25 };
在这个示例中,Person
是一个描述具有name
(string
类型)和age
(number
类型)的对象的接口。
- 可选属性:接口属性可以被标记为可选,使用
?
。
示例:
interface Person {
name: string;
age?: number;
}
let alice: Person = { name: 'Alice' };
在这个示例中,age
是Person
接口中的一个可选属性。即使对象alice
没有age
属性,它仍然是一个Person
。
- 函数类型:接口也可以描述函数类型。
示例:
interface GreetFunction {
(name: string, age: number): string;
}
let greet: GreetFunction = function(name: string, age: number): string {
return `Hello, my name is ${name} and I am ${age} years old.`;
};
在这个示例中,GreetFunction
是一个描述接受一个name
和age
并返回一个string
的函数的接口。
- 扩展接口:接口可以扩展彼此,创建一个继承基接口成员的新接口。
示例:
interface Animal {
name: string;
}
interface Dog extends Animal {
breed: string;
}
let myDog: Dog = { name: 'Rex', breed: 'German Shepherd' };
在这个示例中,Dog
扩展了Animal
,所以Dog
既有name
也有breed
。
TypeScript中的枚举
枚举是定义一组命名常数的方式。它们经常用于表示一组相关值,比如一周的天或一年的月份。
TypeScript支持数值枚举和字符串枚举,提供了灵活的方式来定义和使用一组常数。
- 数值枚举:数值枚举是定义一组具有数值的命名常数的方式。默认情况下,常数的值从0开始,并随着每个后续常数递增1。
示例:
enum Day {
Sunday,
Monday,
Tuesday,
Wednesday,
Thursday,
Friday,
Saturday
}
let today: Day = Day.Monday;
在这个示例中,Day
是一个表示一周天的数值枚举。常数Sunday
、Monday
、Tuesday
等从0开始被分配数值。
- 字符串枚举:字符串枚举是定义一组具有字符串值的命名常数的方式。与数值枚举不同,字符串枚举中的常数的值被初始化为常数名的值。
示例:
enum Month {
January = 'January',
February = 'February',
March = 'March',
April = 'April',
May = 'May',
June = 'June',
July = 'July',
August = 'August',
September = 'September',
October = 'October',
November = 'November',
December = 'December'
}
let currentMonth: Month = Month.June;
在这个示例中,Month
是一个表示一年月份的字符串枚举。常数January
、February
、March
等被赋予等于它们名字的字符串值。
- 计算枚举:枚举可以具有计算值,这些值是用表达式而不是常数值初始化的。这允许在定义常数的值时更加灵活。
示例:
enum Color {
Red = 1,
Green = Math.pow(2, 2),
Blue = Math.pow(2, 3)
}
let color: Color = Color.Green;
在这个示例中,Color
是一个具有计算值的枚举。常数Red
、Green
和Blue
分别被赋予1、4和8的值,使用Math.pow
函数。
- 反向映射:TypeScript中的枚举支持反向映射,这意味着你可以从它的值访问常数的名称。这对于调试和日志记录非常有用。
示例:
enum Day {
Sunday,
Monday,
Tuesday,
Wednesday,
Thursday,
Friday,
Saturday
}
let dayName: string = Day[1]; // 'Monday'
在这个示例中,Day
枚举被用来访问常数的名称。
TypeScript中的泛型
泛型是定义可以与不同数据类型一起使用的函数或类的方式。它们经常用于创建可重用的组件,这些组件可以与不同类型的数据一起工作。
TypeScript支持泛型,允许你编写灵活且类型安全的代码。
让我们来看一些泛型函数和类的示例。
- 泛型函数:泛型函数是可以与各种数据类型一起工作的函数。它们使用类型参数定义,这些类型参数是实际将用于函数调用的类型的占位符。
示例:
function identity<T>(value: T): T {
return value;
}
let result1: number = identity<number>(42);
let result2: string = identity<string>('Hello, TypeScript!');
在这个示例中,identity
是一个泛型函数,它接受一个类型参数T
并返回类型为T
的值。类型参数T
用于指定参数和返回值的类型。
- 泛型类:泛型类是可以与各种数据类型一起工作的类。它们使用类型参数定义,这些类型参数是实际将用于类实例化的类型的占位符。
示例:
class Box<T> {
value: T;
constructor(value: T) {
this.value = value;
}
}
let box1: Box<number> = new Box<number>(42);
let box2: Box<string> = new Box<string>('Hello, TypeScript!');
在这个示例中,Box
是一个泛型类,它接受一个类型参数T
并有一个类型为T
的属性value
。类型参数T
用于指定存储在盒子中值的类型。
- 泛型约束:泛型约束是一种限制可以与泛型函数或类一起使用的类型的方式。它们使用
extends
关键字定义,后跟类型或接口,类型参数必须扩展。
示例:
interface Printable {
print(): void;
}
function printValue<T extends Printable>(value: T): void {
value.print();
}
class Person implements Printable {
print(): void {
console.log('Printing person...');
}
}
let person: Person = new Person();
printValue(person);
在这个示例中,Printable
是一个定义了print
方法的接口。printValue
函数是一个泛型函数,它接受一个必须扩展Printable
的类型参数T
。Person
类实现了Printable
接口。
结论
在本文中,你学习了TypeScript的基础知识。我们讨论了TypeScript的基本语法,比如变量、函数和类。你还学习了TypeScript的内置类型,比如数字、字符串和布尔值。我们讨论了TypeScript的内置枚举,比如数值枚举、字符串枚举和计算枚举。并且你学习了TypeScript的泛型类型,比如泛型函数和类。
感谢阅读!欢迎点赞
、收藏
、关注
,一键三连!!!
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。