[关闭]
@buluoXu 2020-07-03T02:01:57.000000Z 字数 7293 阅读 721

TypeScript入门

TypeScript


什么是 TypeScript

TypeScript 是 JavaScript 的一个超集,主要提供了类型系统和对 ES6 的支持,由 Microsoft 开发,编译为JavaScript。

为啥选择 TypeScript

1·TypeScript 增加了代码的可读性和可维护性

2·TypeScript 非常包容

3·TypeScript 的缺点

Hello TypeScript

demo.ts

  1. function sayHello(person: string) {
  2. return 'Hello, ' + person;
  3. }
  4. let user = 'Tom';
  5. console.log(sayHello(user));

编译为:
demo.js

  1. function sayHello(person) {
  2. return 'Hello, ' + person;
  3. }
  4. var user = 'Tom';
  5. console.log(sayHello(user));

运行

TypeScript基础类型

原始数据类型

布尔值

布尔值是最基础的数据类型,在 TypeScript 中,使用 boolean 定义布尔值类型:

  1. let isDone: boolean = false;
  2. //编译后
  3. var isDone = false;

数值

使用 number 定义数值类型:

  1. let decLiteral: number = 6;
  2. let hexLiteral: number = 0xf00d;
  3. // ES6 中的二进制表示法
  4. let binaryLiteral: number = 0b1010;
  5. // ES6 中的八进制表示法
  6. let octalLiteral: number = 0o744;
  7. let notANumber: number = NaN;
  8. let infinityNumber: number = Infinity;

运行

其中 0b1010 和 0o744 是 ES6 中的二进制和八进制表示法,它们会被编译为十进制数字

字符串

使用 string 定义字符串类型:

  1. let myName: string = 'Tom';
  2. let myAge: number = 25;
  3. // 模板字符串
  4. let sentence: string = `Hello, my name is ${myName}.
  5. I'll be ${myAge + 1} years old next month.`;

运行

空值

JavaScript 没有空值(Void)的概念,在 TypeScript 中,可以用 void 表示没有任何返回值的函数:

  1. function alertName(): void {
  2. alert('My name is Tom');
  3. }
  4. let unusable: void = undefined;

Null 和 Undefined

在 TypeScript 中,可以使用 null 和 undefined 来定义这两个原始数据类型:

  1. let u: undefined = undefined;
  2. let n: null = null;
  3. // 这样不会报错
  4. let num: number = undefined;
  5. // void 类型的变量不能赋值给 number 类型的变量:
  6. let u: void;
  7. let num: number = u;
  8. // Type 'void' is not assignable to type 'number'.

任意值

任意值(Any)用来表示允许赋值为任意类型。

  1. //普通类型,在赋值过程中改变类型是不被允许的
  2. let myFavoriteNumber: string = 'seven';
  3. myFavoriteNumber = 7;
  4. // error TS2322: Type 'number' is not assignable to type 'string'.

但如果是 any 类型,则允许被赋值为任意类型

  1. let myFavoriteNumber: any = 'seven';//字符串
  2. myFavoriteNumber = 7;//数字
  3. myFavoriteNumber = {}//对象
  4. ...

类型推论

若没有明确的指定类型,TypeScript 会依照类型推论(Type Inference)的规则推断出一个类型

什么是类型推论?
以下代码没有指定类型,但是会在编译的时候报错:

  1. let myFavoriteNumber = 'seven';
  2. myFavoriteNumber = 7;
  3. // error TS2322: Type 'number' is not assignable to type 'string'.

运行

事实上,它等价于:

  1. let myFavoriteNumber: string = 'seven';
  2. myFavoriteNumber = 7;
  3. // index.ts(2,1): error TS2322: Type 'number' is not assignable to type 'string'.

运行

TypeScript 会在没有明确的指定类型的时候推测出一个类型,这就是类型推论

如果定义的时候没有赋值,不管之后有没有赋值,都会被推断成 any 类型而完全不被类型检查:

  1. let myFavoriteNumber;
  2. myFavoriteNumber = 'seven';
  3. myFavoriteNumber = 7;

联合类型

联合类型(Union Types)表示取值可以为多种类型中的一种。
联合类型使用 | 分隔每个类型

  1. let myFavoriteNumber: string | number;
  2. myFavoriteNumber = 'seven';
  3. myFavoriteNumber = 7;
  4. //myFavoriteNumber = true;报错

运行

访问联合类型的属性或方法

当 TypeScript 不确定一个联合类型的变量到底是哪个类型的时候,我们只能访问此联合类型的所有类型里共有的属性或方法:

  1. function getLength(something: string | number): number {
  2. return something.length;
  3. }
  4. // index.ts(2,22): error TS2339: Property 'length' does not exist on type 'string | number'.
  5. // Property 'length' does not exist on type 'number'.

length 不是 string 和 number 的共有属性,所以会报错。

访问 string 和 number 的共有属性是没问题的:

  1. function getString(something: string | number): string {
  2. return something.toString();
  3. }

对象的类型——接口

在 TypeScript 中,我们使用接口(Interfaces)来定义对象的类型。

什么是接口?

在面向对象语言中,接口(Interfaces)是一个很重要的概念,它是对行为的抽象,而具体如何行动需要由类(classes)去实现(implement)。

TypeScript 中的接口是一个非常灵活的概念,除了可用于对类的一部分行为进行抽象以外,也常用于对「对象的形状(Shape)」进行描述。

栗子:

  1. interface Person {
  2. name: string;
  3. age: number;
  4. }
  5. let tom: Person = {
  6. name: 'Tom',
  7. age: 25
  8. };

定义Person接口,定义tom变量,它的类型是Person,约束了 tom 的形状必须和接口 Person 一致
运行

尝试增加或者减少属性,会发生什么?

可选属性

不要完全匹配一个形状,那么可以用可选属性:

  1. interface Person {
  2. name: string;
  3. age?: number;
  4. }
  5. let tom: Person = {
  6. name: 'Tom'
  7. };
  8. let tom1: Person = {
  9. name: 'Tom',
  10. age: 25
  11. };

运行

任意属性

需要任意的属性,可以使用如下方式:

  1. interface Person {
  2. name: string;
  3. age?: number;
  4. [propName: string]: any;
  5. }
  6. let tom: Person = {
  7. name: 'Tom',
  8. gender: 'male'
  9. };

运行

使用 [propName: string] 定义了任意属性取 string 类型的值。

一旦定义了任意属性,那么确定属性和可选属性的类型都必须是它的类型的子集

  1. interface Person {
  2. name: string;
  3. age?: number;
  4. [propName: string]: string;
  5. }
  6. let tom: Person = {
  7. name: 'Tom',
  8. age: 25,
  9. gender: 'male'
  10. };

运行

只读属性

用 readonly 定义只读属性:

  1. interface Person {
  2. readonly id: number;
  3. name: string;
  4. age?: number;
  5. [propName: string]: any;
  6. }
  7. let tom: Person = {
  8. id: 89757,
  9. name: 'Tom',
  10. gender: 'male'
  11. };
  12. tom.id = 9527;

运行

注意,只读的约束存在于第一次给对象赋值的时候,而不是第一次给只读属性赋值的时候

数组的类型

在 TypeScript 中,数组类型有多种定义方式,比较灵活。

「类型 + 方括号」表示法

  1. let fibonacci: number[] = [1, 1, 2, 3, 5];//正确
  2. let fibonacci1: number[] = [1, '1', 2, 3, 5];//报错

数组泛型

我们也可以使用数组泛型(Array Generic) Array 来表示数组:

  1. let fibonacci: Array<number> = [1, 1, 2, 3, 5];

用接口表示数组

  1. interface NumberArray {
  2. [index: number]: number;
  3. }
  4. let fibonacci: NumberArray = [1, 1, 2, 3, 5];

类数组

类数组(Array-like Object)不是数组类型,比如 arguments:

  1. function sum() {
  2. let args: number[] = arguments;
  3. }
  4. //Type 'IArguments' is missing the following properties from type 'number[]': pop, push, concat, join, and 24 more.

上例中,arguments 实际上是一个类数组,不能用普通的数组的方式来描述

  1. function sum() {
  2. let args: {
  3. [index: number]: number;
  4. length: number;
  5. callee: Function;
  6. } = arguments;
  7. }

事实上常用的类数组都有自己的接口定义,如 IArguments, NodeList, HTMLCollection 等:

  1. function sum() {
  2. let args: IArguments = arguments;
  3. }

any 在数组中的应用

用 any 表示数组中允许出现任意类型:

  1. let list: any[] = ['xcatliu', 25, { website: 'http://xcatliu.com' }];

函数的类型

函数是 JavaScript 中的一等公民

函数声明

javascript函数声明

  1. // 函数声明(Function Declaration)
  2. function sum(x, y) {
  3. return x + y;
  4. }
  5. // 函数表达式(Function Expression)
  6. let mySum = function (x, y) {
  7. return x + y;
  8. };

一个函数有输入和输出,要在 TypeScript 中对其进行约束,需要把输入和输出都考虑到,其中函数声明的类型定义较简单:

  1. function sum(x: number, y: number): number {
  2. return x + y;
  3. }
  4. sum(1, 2);
  5. sum(1, 2, 3);//参数必须 对应,否则 报错
  6. sum(1)//不能少

函数表达式

如果要我们现在写一个对函数表达式(Function Expression)的定义,可能会写成这样:

  1. let mySum = function (x: number, y: number): number {
  2. return x + y;
  3. };

上面的代码只对等号右侧的匿名函数进行了类型定义,而等号左边的 mySum,是通过赋值操作进行类型推论而推断出来的。如果需要我们手动给 mySum 添加类型,则应该是这样:

  1. let mySum: (x: number, y: number) => number = function (x: number, y: number): number {
  2. return x + y;
  3. };

在 TypeScript 的类型定义中,=> 用来表示函数的定义,左边是输入类型,需要用括号括起来,右边是输出类型。
在 ES6 中,=> 叫做箭头函数

用接口定义函数的形状

可以使用接口的方式来定义一个函数需要符合的形状:

  1. interface SearchFunc {
  2. (source: string, subString: string): boolean;
  3. }
  4. let mySearch: SearchFunc;
  5. mySearch = function(source: string, subString: string) {
  6. return source.search(subString) !== -1;
  7. }

运行

采用函数表达式|接口定义函数的方式时,对等号左侧进行类型限制,可以保证以后对函数名赋值时保证参数个数、参数类型、返回值类型不变。

可选参数

与接口中的可选属性类似,我们用 ? 表示可选的参数:

  1. function buildName(firstName: string, lastName?: string) {
  2. if (lastName) {
  3. return firstName + ' ' + lastName;
  4. } else {
  5. return firstName;
  6. }
  7. }
  8. let tomcat = buildName('Tom', 'Cat');
  9. let tom = buildName('Tom');

运行

可选参数后面不允许再出现必需参数了:

  1. function buildName(firstName?: string, lastName: string) {
  2. if (firstName) {
  3. return firstName + ' ' + lastName;
  4. } else {
  5. return lastName;
  6. }
  7. }
  8. let tomcat = buildName('Tom', 'Cat');
  9. let tom = buildName(undefined, 'Tom');

运行

参数默认值

ES6 中,允许给函数的参数添加默认值,TypeScript 会将添加了默认值的参数识别为可选参数:

  1. function buildName(firstName: string, lastName: string = 'Cat') {
  2. return firstName + ' ' + lastName;
  3. }
  4. let tomcat = buildName('Tom', 'Cat');
  5. let tom = buildName('Tom');

运行

剩余参数

ES6 中,可以使用 ...rest 的方式获取函数中的剩余参数(rest 参数):

  1. function push(array, ...items) {
  2. items.forEach(function(item) {
  3. array.push(item);
  4. });
  5. }
  6. let a: any[] = [];
  7. push(a, 1, 2, 3);

事实上,items 是一个数组。所以我们可以用数组的类型来定义它:

  1. function push(array: any[], ...items: any[]) {
  2. items.forEach(function(item) {
  3. array.push(item);
  4. });
  5. }
  6. let a = [];
  7. push(a, 1, 2, 3);

运行

重载

重载允许一个函数接受不同数量或类型的参数时,作出不同的处理。

需要实现一个函数 reverse,输入数字 123 的时候,输出反转的数字 321,输入字符串 'hello' 的时候,输出反转的字符串 'olleh'。

  1. function reverse(x: number | string): number | string {
  2. if (typeof x === 'number') {
  3. return Number(x.toString().split('').reverse().join(''));
  4. } else if (typeof x === 'string') {
  5. return x.split('').reverse().join('');
  6. }
  7. }

运行

有一个缺点,就是不能够精确的表达,输入为数字的时候,输出也应该为数字,输入为字符串的时候,输出也应该为字符串。

  1. function reverse(x: number): number;
  2. function reverse(x: string): string;
  3. function reverse(x: number | string): number | string {
  4. if (typeof x === 'number') {
  5. return Number(x.toString().split('').reverse().join(''));
  6. } else if (typeof x === 'string') {
  7. return x.split('').reverse().join('');
  8. }
  9. }

运行

结束

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注