typeScript - 基本概念

ts其实也看了好久了,一直打算把ts在项目中用起来,无奈这段时间一直在看node,docker也忘的差不多了,索性在复习一下ts,顺便输出一些笔记记录一下,毕竟自己吃过的东西在看起来也会快一些…….

TypeScript简介

  • TypeScript 是 JavaScript 的一个超集
  • 它可以编译成纯 JavaScript。
  • 编译出来的 JavaScript 可以运行在任何浏览器上。
  • TypeScript 编译工具可以运行在任何服务器和任何系统上。
  • TypeScript 是开源的
  • TypeScript 中,使用 : 指定变量的类型, : 的前后有没有空格都可以。

优点

  • 增加了代码的可读性和可维护性

提供了类型系统和对 ES6 的支持

编译阶段就发现大部分错误

  • 兼容性好

TypeScript 是 JavaScript 的超集, .js 文件可以直接重命名为 .ts 即可

即使不显式的定义类型,也能够自动做出类型推论

即使 TypeScript 编译报错,也可以生成 JavaScript 文件

兼容第三方库,即使第三方库不是用 TypeScript 写的,也可以编写单独的类型文件供 TypeScript 读取

缺点

  • 概念多

有一定的学习成本,需要理解接口(Interfaces)、泛型(Generics)、类(Classes)、枚举类型(Enums)

短期可能会增加一些开发成本,毕竟要多写一些类型的定义

安装

npm install -g typescript

编译一个 TypeScript 文件很简单:tsc xxx.ts

TypeScript 编写的文件以 .ts 为后缀,用 TypeScript 编写 React时,以 .tsx 为后缀

配置

如果一个目录下存在一个tsconfig.json文件,那么它意味着这个目录是TypeScript项目的根目录。

  • tsconfig.json
1
2
3
4
5
6
{
files:{}, //
include:[], //
exclude:[], //
noEmitOnError:true // 在报错的时候终止 js 文件的生成
}

基础

1.原始数据类型

  • js的数据类型分为两类:原始数据类型 - Primitive data types(布尔值,数值,字符串,null,undefined,以及symbol)和对象类型- Object types

布尔值 在 TypeScript 中,使用 boolean定义布尔值类型 ,而 Boolean 是JavaScript 中的构造函数

使用 number 定义数值类型

使用 string 定义字符串类型:

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

1
2
3
4
5
6
// undefined 类型的变量只能被赋值为 undefined , null 类型的变量只能被赋值为 null
let u: undefined = undefined;
let n: null = null;

// 与 void 的区别是, undefined 和 null 是所有类型的子类型。也就是说undefined 类型的变量,可以赋值给 number 类型的变量
let num: number = undefined;
  • 空值

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

1
2
3
function alertName(): void {
alert('My name is pis');
}

2. 任意值

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

如果是一个普通类型,在赋值过程中改变类型是不被允许的:但如果是 any 类型,则允许被赋值为任意类型。

在任意值上访问任何属性都是允许的,也允许调用任何方法:

1
2
3
let anyThing: any = 'hello';
console.log(anyThing.myName); // 访问任何属性,即使不存在
anyThing.setName('Jerry'); // 调用任何方法,即使不存在
  • 可以认为,声明一个变量为任意值之后,对它的任何操作,返回的内容的类型都是任意值

  • 变量如果在声明的时候,未指定其类型,那么它会被识别为任意值类型

3. 类型推论 - Type Inference

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

  • 如果没有明确的指定类型,那么 TypeScript 会依照类型推论(Type Inference)的规则推断出一个类型。

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

1
2
3
4
5
6
let myFavoriteNumber; // 只定义 没赋值  any类型不会进行类型推论
myFavoriteNumber = 'seven';
myFavoriteNumber = 7;

let myFavoriteNumber = 'seven'; // 定义并且复制但是没用显示指定类型,会进行类型推论
myFavoriteNumber = 7;

4. 联合类型 - Union Types

  • 联合类型(Union Types)表示取值可以为多种类型中的一种,但是不能是其他类型

  • 联合类型使用 | 分隔每个类型。

1
2
3
let myFavoriteNumber: string | number;
myFavoriteNumber = 'seven';
myFavoriteNumber = 7;
  • 当 TypeScript 不确定一个联合类型的变量到底是哪个类型的时候,我们只能访问此联合类型的所有类型里共有的属性或方法
1
2
3
4
function getLength(something: string | number): number {
return something.length; // length 不是 string 和 number 的共有属性,所以会报错。
return something.toString(); // toString则可以访问
}

5. 对象的类型——接口 Interfaces

  • 接口(Interfaces)是对行为的抽象,而具体如何行动需要由类(classes)去实现(implements)

  • 接口一般首字母大写,有的编程语言中会建议接口的名称加上 I 前缀。

  • 定义的变量比接口少了一些属性是不允许的,多一些属性也是不允许的,可见,赋值的时候,变量的形状必须和接口的形状保持一致

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

确定属性

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

可选属性

  • 有时我们希望不要完全匹配一个形状,那么可以用可选属性:

可选属性的含义是该属性可以不存在,这时仍然不允许添加未定义的属性

1
2
3
4
5
6
7
8
interface Person {
name: string; // 确定属性
age?: number; // 可选属性
}

let tom: Person = {
name: 'Tom'
};

任意属性

  • 有时候我们希望一个接口允许有任意的属性,可以使用如下方式:
1
2
3
4
5
6
7
8
9
10
11
interface Person {
readonly id: number; // 我是只读属性
name: string; // 我是确定属性
age?: number; // 我是可选属性
[propName: string]: any; // 我是任意属性
}

let tom: Person = {
name: 'Tom',
gender: 'male'
};

需要注意的是,一旦定义了任意属性,那么确定属性和可选属性都必须是它的子属性:

1
2
3
4
5
6
7
8
9
10
11
interface Person {
name: string;
age?: number; // age的number类型不是任意类型string的子集,所以会报错
[propName: string]: string;
}

let tom: Person = {
name: 'Tom',
age: 25,
gender: 'male'
};

只读属性

  • 有时候我们希望对象中的一些字段只能在创建的时候被赋值,那么可以用readonly 定义只读属性:

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

1
2
3
4
5
6
7
8
9
10
11
12
13
interface Person {
readonly id: number; // 只读属性
name: string;
age?: number;
[propName: string]: any;
}

let tom: Person = { // 第一次给对象赋值的,没对只读属性赋值,后面就不能赋值了
name: 'Tom',
gender: 'male'
};

tom.id = 89757; // 第一次给只读属性复制,报错

6. 数组的类型

  • 1)「类型 + 方括号」表示法 let fibonacci: number[] = [1, 1, 2, 3, 5];

数组的项中不允许出现其他的类型

数组的一些方法的参数也会根据数组在定义时约定的类型进行限制

1
2
fibonacci: number[] = [1, 1, 2, 3, 5];
fibonacci.push('8'); // 不是number
  • 2) 数组泛型(Array Generic) Array 来表示数组

let fibonacci: Array = [1, 1, 2, 3, 5];

  • 3) 用接口表示数组
1
2
3
4
5
interface NumberArray {
[index: number]: number;
}
let fibonacci: NumberArray = [1, 1, 2, 3, 5];
// NumberArray 表示:只要 index 的类型是 number ,那么值的类型必须是number 。
  • any 在数组中的应用

用 any 表示数组中允许出现任意类型 let list: any[] =[‘xx’,1,true]

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

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

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

7. 函数的类型

  • 函数声明(Function Declaration)
1
2
3
4
// 函数声明(Function Declaration)
function sum(x, y) {
return x + y;
}
  • 一个函数有输入和输出,要在 TypeScript 中对其进行约束,需要把输入和输出都考虑到,其中函数声明的类型定义较简单
1
2
3
4
5
function sum(x: number, y: number): number {
return x + y;
}

let result:number = sum(1,2) // 输入多余的(或者少于要求的)参数,是不被允许的
  • 函数表达式(Function Expression)
1
2
3
4
// 函数表达式(Function Expression)
let mySum = function (x, y) {
return x + y;
};
1
2
3
4
5
// 在 TypeScript 的类型定义中, => 用来表示函数的定义
let mySum: (x: number, y: number) => number = function (x: number, y: number): number {
return x + y;
};
// 左边是输入类型 右边是输出类型

可选参数

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

  • 可选参数必须接在必需参数后面(可选参数有了默认值就不受「可选参数必须接在必需参数后面」的限制了)

1
2
3
4
5
6
7
8
9
10
function buildName(firstName: string, lastName?: string) { // lastName表示可选的参数
if (lastName) {
return firstName + ' ' + lastName;
} else {
return firstName;
}
}

let tomcat = buildName('Tom', 'Cat');
let tom = buildName('Tom');

参数默认值

  • TypeScript 会将添加了默认值的参数识别为可选参数
1
2
3
4
5
6
function buildName(firstName: string, lastName: string = 'Cat'){
return firstName + ' ' + lastName;
}

let tomcat = buildName('Tom', 'Cat');
let tom = buildName('Tom');

剩余参数

  • …rest 的方式获取函数中的剩余参数

  • rest 参数只能是最后一个参数

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

1
2
3
4
5
function restFun(firstArg:number[],...argus:any[]){
argus.forEach(item=>{
console.log(item)
})
}

使用接口定义函数

  • 我们也可以使用接口的方式来定义一个函数需要符合的形状:
1
2
3
4
5
6
7
8
interface SearchFunc {
(source: string, subString: string): boolean;
}

let mySearch: SearchFunc;
mySearch = function(source: string, subString: string) {
return source.search(subString) !== -1;
}

8. 重载 overload

  • 重载允许一个函数接受不同数量或类型的参数时,作出不同的处理
1
2
3
4
5
6
7
8
9
10
function reverse(x: number): number;
function reverse(x: string): string;
function reverse(x: number | string): number | string {
if (typeof x === 'number') {
return Number(x.toString().split('').reverse().join(''))
;
} else if (typeof x === 'string') {
return x.split('').reverse().join('');
}
}
  • TypeScript 会优先从最前面的函数定义开始匹配,所以多个函数定义如果有
    包含关系,需要优先把精确的定义写在前面

9. 类型断言 Type Assertion

  • 可以用来手动指定一个值的类型

  • <类型>值 或者 值 as 类型

  • 在 tsx 语法(React 的 jsx 语法的 ts 版)中必须用后一种 值 as 类型
1
2
3
4
function getLength(something: string | number): number {
return something.length;
}
// something是联合类型,所以number没有length方法

此时可以使用类型断言,将 something 断言成 string :

1
2
3
4
5
6
7
function getLength(something: string | number): number {
if ((<string>something).length) { // - 类型断言的用法如上,在需要断言的变量前加上 <Type> 即可
return (<string>something).length;
} else {
return something.toString().length;
}
}
  • 类型断言的用法如上,在需要断言的变量前加上 即可

  • 类型断言不是类型转换,断言成一个联合类型中不存在的类型是不允许的:

1
2
3
function toBoolean(something: string | number): boolean {
return <boolean>something; // 报错
}

10.声明文件

  • 当使用第三方库时,我们需要引用它的声明文件,才能获得对应的代码补全、接口提示等功能。

声明语句

  • 我们需要使用 declare var 来定义它的类型
1
2
declare var jQuery: (selector: string) => any; // 定义了jQuery是一个包含selector为string的函数,返回值为any
// declare var 并没有真的定义一个变量,只是定义了全局变量jQuery 的类型,仅仅会用于编译时的检查,在编译结果中会被删除。

有了上面的声明语句,ts才能在编译的时候认识第三方导入的jQuery

声明文件

  • 通常我们会把声明语句放到一个单独的文件( jQuery.d.ts )中,这就是声明文件

声明文件必需以 .d.ts 为后缀。

1
2
// src/jQuery.d.ts
declare var jQuery: (selector: string) => any;

第三方声明文件

  • 更推荐的是使用 @types 统一管理第三方库的声明文件
1
npm install @types/jquery --save-dev // 以 jQuery举例:

11. 内置对象

  • JavaScript 中有很多内置对象,它们可以直接在 TypeScript 中当做定义好了的类型

  • 内置对象是指根据标准在全局作用域(Global)上存在的对象

用 TypeScript 写 Node.js

  • ECMAScript 标准提供的内置对象有:Boolean 、 Error 、 Date 、 RegExp 等
1
2
3
4
let b: Boolean = new Boolean(1);
let e: Error = new Error('Error occurred');
let d: Date = new Date();
let r: RegExp = /[a-z]/;
  • DOM 和 BOM 提供的内置对象有:Document 、 HTMLElement 、 Event 、 NodeList 等。
1
2
3
4
5
let body: HTMLElement = document.body;
let allDiv: NodeList = document.querySelectorAll('div');
document.addEventListener('click', function(e: MouseEvent) {
// Do something
})

Node.js 不是内置对象的一部分,如果想用 TypeScript 写 Node.js,则需要引入第三方声明文件:npm install @types/node –save-dev

初到贵宝地,有钱的给个钱场,没钱的挤一挤给个钱场