TypeScript 速成课

TypeScript 速成课
0

原文:A crash course in TypeScript
作者:Gabriel Tanner
译者:C 君
校对者:ZhichengChen

TypeScript 是 JavaScript 的类型超集,旨在简化大型 JavaScript 应用程序的开发。TypeScript 加入了常见的概念,例如类(classes),泛型(generics),接口(interfaces)和静态类型(static types)并允许开发人员使用静态检查和代码重构等工具。

为什么关注 TypeScript:

现在的问题仍然是为什么你应该优选使用 TypeScript。这有一些关于为什么 JavaScript 开发者应该考虑学习 TypeScript 的原因。

静态类型:

JavaScript 是动态类型的,这意味着直到在运行时实例化时,它不知道变量的类型,这可能导致项目中的问题和错误。TypeScript 加入了对 JavaScript 静态类型支持,如果你正确的使用它处理由变量类型的错误设定引起的错误。你仍然可以完全控制输入代码的严格程度,或者甚至根本不使用类型。

更好的 IDE 支持:

TypeScript 相比 JavaScript 一个更大的优势是更好的 IDE 支持,包括了来自 TypeScript 编译器智能,实时的提示,调试以及更多功能。这里还有一大堆扩展进一步提升你的 TypeScript 开发体验。

应用新的 ECMAScript 特性:

TypeScript 使你可以使用最新的 ECMAScript 功能,并将它们转换到你选择的 ECMAScript 目标。这意味着你可以使用最新的工具开发应用程序,而无需担心浏览器支持。

什么时候你该使用它:

到目前为止,我们应该知道为什么 TypeScript 是有用的以及如何改善我们的开发体验。但它并不是解决所有问题的方法,当然也不能阻止你自己编写可怕的代码。那么让我们来看看你应该在哪里使用 TypeScript。

当你拥有一个很大的代码库时:

TypeScript 是大型代码库的一个很好的补充,因为它可以帮助你防止许多常见错误。这尤其适用于多个开发人员工作在同一项目之中。

当你项目成员早已知道静态类型语言时:

另一个明显使用 TypeScript 的场景是当你和你的团队已经知道静态类型的语言像 Java 和 C# 不想改为编写 JavaScript。

设置/建立:

要设置 TypeScript,我们只需要使用 npm 包管理器安装它并创建一个新的 TypeScript 文件。

npm install -g typescript

安装完成之后我们可以继续探寻 TypeScript 提供给我们的语法和功能特性。

类型:

现在让我们来看看 TypeScript 所提供的类型:

数值(Number):

Typescript 所有的值类型都是浮点数。所有的数字包括二进制和十六进制都是数值类型。

let num: number = 0.222;
let hex: number = 0xbeef;
let bin: number = 0b0010;

字符串(String):

与其他语言一样,TypeScript 使用 String 数据类型来保存文本数据。

let str: string = 'Hello World!';

你还可以用反引号``来应用多行字符串并嵌入表达式。

let multiStr: string = `A simple
multiline string!`
let expression = 'A new expression'
let expressionStr: string = `Expression str: ${ expression }`

布尔类型(Boolean)

TypeScript 支持所有的基本数据类型,布尔类型,值必须为 true 或者 false。

let boolFalse: boolean = false;
let boolTrue: boolean = true;

指定类型

现在我们已经有了基本的数据类型,我们可以看看你如何在 TypeScript 中指定类型。基本上,你只需要在名称和冒号后面写出变量的类型。

单一类型:

这里例子为我们如何为变量指定字符串数据类型

let str: string = 'Hello World'

所有其他数据类型也是这样使用。

多类型:

你仍然可以通过 | 操作符为你的变量指定多个数据类型:

let multitypeVar: string | number = 'String'
multitypeVar = 20

这里我们使用 | 为变量分配两种类型。现在我们可以在其中存储字符串和数值。

类型检测:

现在让我们看看如何检查变量是否具有正确的类型。我们有多种选择,但在这里我只展示了两个最常用的选项。

Typeof:

typeof 仅仅知道基本类型。这意味着它只能检查变量是否是我们上面定义的数据类型之一。

let str: string = 'Hello World!'
if(typeof str === number){
 console.log('Str is a number')
} else {
 console.log('Str is not a number')
}

在此示例中,我们创建一个字符串类型变量并使用 typeof 命令检查 str 是否为 Number 类型(始终为 false)。然后我们打印是否是数值。

Instanceof:

instanceof 运算符与 typeof 几乎相同,只是它还可以检查 JavaScript 尚未定义的自定义类型。

class Human{
 name: string;
 constructor(data: string) {
  this.name = data;
 }
}
let human = new Human('Gabriel')
if(human instanceof Human){
 console.log(`${human.name} is a human`)
}

在这里,我们创建一个自定义类型,我们稍后将在本文中讨论,然后创建它的实例。之后,我们检查它是否真的是 Human 类型的变量,如果是,则在控制台中打印。

类型断言:

有时我们还需要将变量转换为特定的数据类型。这经常发生在你已经指定了一个泛型类型像 any 并且你想使用它具体的类型的方法。

有很多选择可以解决这个问题,但在这里我只分享其中两个。

As 关键字

通过在变量名之后使用 as 关键字跟随具体的数据类型来转换变量的类型。

let str: any = 'I am a String'
let strLength = (str as string).length

这里我们将 str 变量转换为字符串,以便我们可以使用 length 属性(如果您的 TSLINT 设置允许,甚至可以在没有转换的情况下工作)。

<> 操作符:

我们也可以使用 <> 运算符,它与 as 关键字具有完全相同的效果,只有语法差异。

let str: any = 'I am a String'
let strLength = (<string>str).length

此代码块与上面的代码块具有完全相同的功能。它只是语法不同。

数组:

TypeScript 中的数组是相同对象的集合,可以用两种不同的方式创建。

创建数组

使用 []:

我们可以通过指定类型后跟 [] 来定义数组对象,以表示它是一个数组。

let strings: string[] = ['Hello', 'World', '!']

在这个例子中,我们创建一个字符串数组,它包含三个不同的字符串值。

使用泛型数组:

我们还可用指定 Array 定义泛型数组

let numbers: Array<number> = [1, 2, 3, 4, 5]

这里我们创建一个数值数组,它包含 5 个不同的数字。

多(混合)类型数组

此外,我们还可以使用 | 操作符将多个类型分配给单个数组。

let stringsAndNumbers: (string | number)[] = ['Age', 20]

此例中我们创建了一个数值可以包含字符串和数值。

多维数组:

TypeScript 还允许我们定义多维数组,这意味着我们可以将数组保存在另一个数组中。我们可以通过使用多个 [] 运算符来创建一个多维数组。

let numbersArray: number[][] = [[1,2,3,4,5], [6,7,8,9,10]]

这里我们创建一个包含另一个数字数组的数组。

元组(Tupels)

元组基本类似数组但有一点不同。我们可以定义每个位子上储存数据的类型。这意味着我们可以通过方括号内的枚举来限制固定索引位置的类型。

let exampleTuple: [number, string] = [20, 'https://google.com'];

在此列中,我们定义了一个简单的元组,在索引 0 位置上指定为数值类型,在索引为 1 位置上指定为字符串类型。这意味着如果我们尝试在此索引上放置另一种数据类型,则会抛出错误。

以下是非法元组的示例:

const exampleTuple: [string, number] = [20, 'https://google.com'];

枚举(Enums):

与大多数其他面向对象编程语言一样,TypeScript 中的枚举允许我们定义一组命名常量。 TypeScript 还提供基于数值和基于字符串的枚举。使用 enum 关键字定义 TypeScript 中的枚举。

数值枚举:

首先,我们将查看数值枚举,其中我们将键值与索引匹配。

enum State{
 Playing = 0,
 Paused = 1,
 Stopped = 2
}

上面,我们定义了数值枚举将 Playing 初始化为 0,Paused 为 1 等等。

enum State{
 Playing,
 Paused,
 Stopped
}

我们也可以将初始化器留空,而 TypeScript 会从零开始自动索引它。

字符串枚举

定义字符串枚举也十分简单,我们只需要在定义的每个枚举值后初始化字符串值。

enum State{
 Playing = 'PLAYING',
 Paused = 'PAUSED',
 Stopped = 'STOPPED'
}

这里我们通过使用字符串初始化我们的状态来定义字符串枚举。

对象(Objects):

TypeScript 中的对象是包含一组键值对的实例。这些值可以是变量,数组甚至函数。它也被视为表示非基本类型的数据类型。

我们可以使用大括号创建一个对象:

const human = {
 firstName: 'Frank',
 age: 32,
 height: 185
};

这里我们创建了一个 human 对象包含三个不同的键值对。

我们可以为对象加入方法:

const human = {
 firstName: 'Frank',
 age: 32,
 height: 185,
 greet: function(){
  console.log("Greetings stranger!")
 }
};

自定义类型

TypeScript 还允许我们自定义类型,以便于我们后续重用。要创建自定义类型,我们只需要使用 type 关键字并定义我们的类型。

type Human = {firstName: string, age: number, height: number}

在此示例中,我们定义了一个名为 Human 包含三个属性的自定义类型。现在让我们看看如何创建这种类型的对象。

const human: Human = {firstName: ‘Franz’, age: 32, height: 185}

在这里,我们创建自定义类型的实例并设置所需的属性。

方法参数和返回类型:

TypeScript 允许我们为方法参数和返回值指定数据类型。现在让我们看一下使用 TypeScript 定义函数的语法。

function printState(state: State): void {
 console.log(`The song state is ${state}`)
}
function add(num1: number, num2: number): number {
 return num1 + num2
}

这里我们有两个示例函数,它们都具有定义类型的参数。我们还看到在结束括号后定义返回类型。

现在我们可以像普通的 JavaScript 一样调用我们的函数,但编译器会检查我们是否为函数提供了正确的参数。

add(2, 5)
add(1) // Error to few parameters
add(5, '2') // Error the second argument must be type number

可选属性:

TypeScript 允许我们为方法(注:接口等同样可以定义可选属性)定义可选属性。我们通过 ? 操作符定义。

function printName(firstName: string, lastName?: string) {
if (lastName) 
 console.log(`Firstname: ${firstName}, Lastname: ${lastName}`);
else console.log(`Firstname: ${firstName}`);
}

在这个例子中,lastName 是一个可选参数,这意味着当我们不提供调用函数时,我们不会从编译器中获得错误。

printName('Gabriel', 'Tanner')
printName('Gabriel')

这表示 2 个示例都被视为正确的。

默认值:

我们使用可选属性的第二种方法是为它指定一个默认值。我们可以通过直接在函数头部赋值来实现。

function printName(firstName: string, lastName: string = 'Tanner') {
 console.log(`Firstname: ${firstName}, Lastname: ${lastName}`);
}

在此例我中我们 lastName 赋予了默认值这意味着我们不必每次调用方法时提供它。

接口(Interfaces):

Typescript 中的接口用于定义与我们的代码以及项目之外的代码的契约。接口只包含我们的方法和属性的声明,但不实现它们。实现方法和属性是实现接口的类的责任。

让我们看个例子让定义更加清晰:

interface Person{
 name: string
}
const person: Person = {name: 'Gabriel'}
const person2: Person = {names: 'Gabriel'} // is not assignable to type Person

这里我们声明了一个接口包含一个 name 属性,它需要在实现接口时实现。这就是为什么 person2 变量会抛出异常

可选属性

在 TypeScript 中,有时并不是所有接口属性都是必需的。可以使用 ? 运算符在属性后面将其设置为可选。

interface Person{
 name: string
 age?: number
}
const person: Person = {name: 'Frank', age: 28}
const person2: Person = {name: 'Gabriel'}

在这里,我们创建一个具有一个普通和一个可选属性的接口,该属性是使用 ? 运算符。这就是我们两个人初始化都有效的原因。

只读属性

我们的接口中一些属性也应该只在首次创建对象时修改赋值。我们可以通过将 readonly 关键字放在我们的属性名称之前来指定此功能。

interface Person{
 name: string
 readonly id: number
 age?: number
}
const person: Person = {name: 'Gabriel', id: 3127831827}
person.id = 200 // Cannot assign to id because it is readonly

在此示例中,id 属性是只读的,在创建对象后无法更改。

模块(Barrels Modules):

Barrels 允许我们在一个更方便的模块中汇总多个导出模块。

我们仅需要创建一个新文件,它将导出我们项目中的多个模块 (译者注:根据 ECMAScript 定义一个文件定义一个模块,此处可能表示模块聚合(类似库等的入口文件))。

export * from './person';
export * from './animal';
export * from './human';

之后我们可以通过这个便利的单一导入语句引入这些模块。

import { Person, Animal, Human } from 'index';

泛型(Generics):

泛型允许我们创建兼容广泛类型而不是单一类型的组件。这使得我们的组件“开放”和复用。

现在你可能想知道为什么我们不只是使用任何( any )类型来使组件接受多种类型而不是单一类型。让我们看一个例子更好地了解。

我们想要一个简单的假函数(dummy function),它返回传入的参数:

function dummyFun(arg: any): any {
 return arg;
}

然而 any 是通用的,某种程度它能接受所有类型参数但有一个很大的区别。我们丢失了我们传入的参数是什么类型以及返回值是什么类型。

所以让我们来看看,我们如何接受所有类型并知道它返回值的类型。

function dummyFun<T>(arg: T): T {
 return arg
}

这里我们使用泛型参数 T,因此我们可以捕获变量类型并在以后使用它。我们还使用它作为返回参数类型,它允许我们在检查代码时看到相应的类型。

更多详细介绍你可以查看 Charly Poly 关于 Generics and overloads 的文章

访问修饰符(Access Modifiers):

访问修饰符控制我们类成员的可访问性。 Typescript 支持三种访问修饰符 - 公共的(public),私有的(private)和受保护的(protected)。

公共的:

公共成员可以在任何地方访问,没有任何限制 这也是标准修饰符,这意味着您不需要使用 public 关键字为变量添加前缀。

私有的:

私有成员只能在其定义的类中能访问。

受保护的:

保护成员只能在其定义的类及其子类中访问。

TSLINT:

TSLINT 是 Typescript 的标准 linter,可以帮助我们编写干净,可维护和可读的代码。它可以使用我们自己的 lint 规则,配置和格式化程序进行自定义。

设置:

首先我们需要安装 TypeScript 和 tslint,我们可以全局安装和局部安装:

npm install tslint typescript --save-dev
npm install tslint typescript -g

之后,我们可以使用 TSLINT CLI 在我们的项目中初始化 TSLINT。

tslint --init

现在我们有了 tslint.json 文件,我们可以开始配置我们的规则了。

配置:

TSLINT 允许使用配置我们自己的规则并自定义代码的外观。默认情况下,tslint.json 文件看起来像这样,只使用默认规则。

{
"defaultSeverity": "error",
"extends": [
 "tslint:recommended"
],
"jsRules": {},
"rules": {},
"rulesDirectory": []
}

我们可以通过将它们放在 rules 对象中来添加其他规则。

"rules": {
 "no-unnecessary-type-assertion": true,
 "array-type": [true, "array"],
 "no-double-space": true,
 "no-var-keyword": true,
 "semicolon": [true, "always", "ignore-bound-class-methods"]
},

有关所有可用规则的概述,你可以查看官方文档

推荐阅读:

An introduction to the JavaScript DOM
An introduction to the JavaScript DOM

总结

希望这篇文章帮助你理解 TypeScript 的基础以及如何在项目中使用。

如果你觉得这个有用,请考虑推荐并与其他开发者共享。

如果你有任何问题和反馈,在以下评论中让我知道。