首次接触 TypeScript

什么是 Typescript?

TypeScript 是 javascript 的超集
TypeScript 和 Javascript 一样吗?既是也不是。TypeScript 是由 Microsoft 创建的,并且建立在 javascript 之上。简而言之,它与 Javascript 相同,但增加了一些功能。你从 Javascript 中了解的所有内容在 TypeScript 中都会有用。

我应该先学哪一个?

你肯定会先学习 Javascript。如果你不了解 Javascript,那么你将很难学习和理解 TypeScript。为什么要创建一种新语言?Javascript 没问题,不是吗?当人们开始在复杂的应用程序中使用 JavaScript 时,他们很快意识到 JavaScript 在 OOP 方面变得难以使用,并且很难找到一些错误。
TypeScript 是由 Microsoft 开发的,用于弥补这一差距。那么 TypeScript 究竟为 javascript 添加了什么?

  • 强大的类型系统
  • 开发时进行类型错误检查
  • 好的面向对象编程
  • 新功能,如接口、泛型等。
  • 元编程,如装饰器
  • 编译为可在旧浏览器上运行的 javascript
  • 编辑器中的代码自动完成
  • 还有更多…

还有什么我应该知道的?
TypeScript 不能像 Javascript 那样在浏览器中或与 node.js 一起运行。要执行,TypeScript 需要转换/编译为 Javascript。

使用 TypeScript 需要使用编译器。例如,如果你有一个文件名 app.ts,TypeScript 编译器将创建 javascript 等效的 app.js。它将用于运行你的应用程序。

这就是为什么我们说 TypeScript 在开发时有帮助。

如何安装和使用 TypeScript 编译器你可以使用此命令在开发机器上全局安装 TypeScript

1
$ npm install -g typescript

执行编译器

1
2
3
4
$ tsc app.js

// or watch mode
$ tsc app.js -w

在监视模式下,每次保存时,TypeScript 都会自动在 app.js 中重新编译 app.ts。

TypeScript 配置

TypeScript 有很多可用的配置/设置。我不会在这篇介绍文章中介绍这些,只是想让你知道 TypeScript 设置存储在一个名为 tsconfig.json 的文件中。你可以使用以下命令创建此文件

1
$ tsc --int

TypeScript 学习

你现在将学习如何使用基本的 TypeScript 功能

核心类型

TypeScript 最有价值的功能之一是类型系统。在 TypeScript 中,你可以将类型分配给变量,如果代码中任何地方不遵循该类型,TypeScript 编译器将抛出错误。

为了了解类型,我们将进行 TypeScript 与 Javascript 的比较。

这是常规的 Javascript 代码

1
2
3
4
5
6
function add(num1, num2) {
return num1 + num2
}

const result1 = add(10, 20). // 30
const result2 = add("10", "20") // 1020

在此示例中,result1 将为 30,result2 将为 1020

为什么 result2 不是 30?由于你提供了双引号,因此 Javascript 认为你的参数是字符串,因此会使用该逻辑执行代码而不会报告任何错误。

现在想象一下这种错误会对会计应用程序造成什么样的损害。在 10 万行代码的 Web 应用程序中查找这种错误非常困难、令人沮丧且耗时。

TypeScript 来救场!让我们使用上面相同的代码,但使用 TypeScript
唯一的区别是参数名称后面添加了 :number 类型

在此示例中,‘const result2 = add(“10”, “20”)’ 行将在代码编辑器和编译时报告错误。

类型推断

当初始化变量时,TypeScript 可以自动推断/检测变量的类型

1
2
3
let amount: number = 99.95;
// same as
let amount = 99.95; // best practice

两个变量都是数字类型。最佳做法是让 TypeScript 推断完成其工作,因为我们自己设置了初始值。这有助于避免重复代码。

请注意,我们仅在变量未用值初始化时指定类型

1
2
let title: string;
title = "Hello World";

对象类型

TypeScript 也会自动推断对象类型

1
2
3
4
const person = {
name: "William",
age: 45,
};

将导致 TypeScript 对象类型

1
2
3
4
5
6
7
const person: {
name: string,
age: number,
} = {
name: "William",
age: 45,
};

数组类型

声明数组的语法是:类型

1
2
const names: string[] = ["William", "John", "Paul"];
const amounts: number[] = [100, 200, 300];

元组类型

当我们需要数组中固定数量的值时使用。

1
const names: [number, string] = [100, "William"];

枚举类型

枚举主要用于给常量分配名称

1
2
enum Role { ADMIN, READ_ONLY, AUTHOR }
console.log(Role.ADMIN) // 0

还可以指定 key(key 可以是任意类型)

1
2
enum Role { ADMIN = 100, READ_ONLY = 200, AUTHOR = 300 }
console.log(Role.ADMIN) // 100

任何类型

如果你确实不知道类型,请使用 any 作为后备。

1
2
3
let title: any;
title = 25;
title = "Hello World";

请注意这不是一个好的做法。尽量避免!

联合类型

变量可以灵活地分配两种类型

1
2
3
4
5
6
7
function combine(item1: string | number, item2: string | number) {
if (typeof item1 === "number" && typeof item2 === "number") {
console.log(item1 + item2);
} else {
console.log(item1.toString() + item2.toString());
}
}

联合类型的语法是:type2 | type2

类型别名

我们可以创建一个自定义类型作为别名,例如联合类型

1
2
3
4
5
6
type Dual = number | string;

let title: Dual;

title = "Hello";
title = 100;

对象类型别名

1
2
3
4
5
6
7
8
9
10
11
12
type User = { name: string; age: number }
const user1: User { name: 'William', age: 25 }

// the syntax is then simplyfy
function loadUser(user: User) {
..do something...
}

// instead of
function loadUser(user { name: stringl age: number }) {
..do something...
}

函数返回类型

我们可以指定函数的返回类型

1
2
3
function add(num1: number, num2: number): number {
return num1 + num2;
}

void 返回类型当函数不返回任何值时,TypeScript 将推断该函数为“void”类型

1
2
3
function displayMessage(): void {
console.log("Hi there");
}

函数类型

声明语法为:(var: type, var: type) ⇒ 返回类型

1
2
3
4
5
6
7
8
9
10
function add(num1: number, num2: number): number {
return num1 + num2;
}

let calc: Function;

// or more specific
let calc: (num1: number, num2: number) => number;
calc = add;
console.log(calc(10, 20));

未知类型

除非我们检查赋值的类型,否则类型未知的变量将无法赋值。

1
2
3
4
let userInput: unknown;
if (typeof userInput === "string") {
userName = userInout;
}

TypeScript 中的 OOP

类声明

1
2
3
4
5
6
7
8
9
10
11
class Product {
name: string;
price: number;

constructor(name: string, price: number) {
this.name = name;
this.price = price;
}
}

const product1 = new Product("iPad", 500);

简写属性初始化

1
2
3
4
5
6
7
8
class Product {

constructor(private name: string, private price: number) {

}
}

const product1 = new Product('iPad', 500)

访问修饰符

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Product {
private name: string;
private price: number;

constructor(name: string, price: number) {
this.name = name
this.price = price
}

public displayProduct() {
console.log(this.name, this.price)
}
}

const product1 = new Product('iPad', 500)

public 关键字是可选的,因为如果没有提供,它是默认修饰符。

  • public 表示类外可用的变量或函数
  • private 表示类外不可用的变量或函数
  • readonly 表示变量为 private 和 readonly
  • protected 表示仅在类或子类内可用的变量或函数

继承

1
class Friends extends Person {}

Getters 和 Setters

1
2
3
4
5
class Friend {
get name() {}

set name(value: string) {}
}

静态属性和方法

1
2
3
4
5
6
7
8
class Product {
static defaultName = 'Product x'
static display name() {
console.log(defaultName)
}
}

Person.display('iPad')

接口

1
2
3
4
5
6
7
interface IsPerson {
name: string;
age: number;
speak(a: string) {
console.log(a)
}
}
1
2
3
4
5
6
7
const me: IsPerson = {
name: "William",
age: 25,
speak(text: string): void {
console.log(text);
},
};
1
2
3
4
5
6
7
8
9
10
11
12
13
class customer implements IsPerson {
private name
private age

constructor(name: string, age: number) {
this.name = name
this.age = age
}

public speak(text: string): void {
console.log(text)
}
}

泛型

编写程序时,最重要的方面之一是构建可重用组件。这可确保程序具有灵活性,并且长期可扩展。

泛型提供了一种创建可重用组件的方法。泛型提供了一种使组件能够处理任何数据类型而不局限于一种数据类型的方法。因此,组件可以与各种数据类型一起调用或使用。

例如,如果我们想创建一个具有可以包含不同对象类型的数据属性的接口

首先创建接口

1
2
3
4
5
interface Person<T> {
name: string;
age: number;
data: T;
}

<T> 是 TypeScript 在编译时添加的类型的占位符

然后你可以在代码中使用通用接口

1
2
3
4
5
6
7
8
9
10
11
12
const person1: Person<string> = {
name: "William",
age: 25,
data: "Info about person",
};

// or
const person1: Person<string[]> = {
name: "William",
age: 25,
data: ["Info about person", "info2"],
};

在上面的例子中,使用了相同的接口来存储字符串和字符串数组。