TypeScript 是 JavaScript 的一个超集,主要提供了类型系统和对 ES6 的支持,它由 Microsoft 开发,代码开源于 GitHub 上。
官网:https://www.tslang.cn/
特点:- 可以在编译阶段就发现大部分错误,这总比在运行时候出错好
- 即使 TypeScript 编译报错,也可以生成 JavaScript 文件
- Google 开发的 Angular 就是使用 TypeScript 编写的
- TypeScript 拥抱了 ES7 规范,也支持部分 ES8 草案的规范
有一定的学习成本,需要理解接口(Interfaces)、泛型(Generics)、类(Classes)、枚举类型(Enums)等前端工程师可能不是很熟悉的东西
短期可能会增加一些开发成本,多写一些类型的定义,长期维护的项目,TypeScript 能够减少其维护成本
安装:
npm install -g typescript
以上命令将会安装typescript编译器和可执行程序(tsc),并且添加到环境变量的全局路径中 tsc -v
编译一个 TypeScript 文件:
tsc hello.ts 类型不匹配时,编辑报错,但可以生成js(编辑通过),如果不希望编译通过需要配饰tsconfig.json
发现在hello.ts同一目录下出现一个hello.js文件,这个文件就是ts编译器输出的内容,其中的js代码与编写的ts代码等价。
可用以下命令来运行:
node hello.js
若想要把编译与运行结合起来,可使用ts-node模块:
npm install -g ts-node
ts-node hello.ts
原始数据类型:boolean、number、string、null、undefined、symbol、void、any
内置对象类型: Boolean Error Date RegExp Math Array
Document HTMLElementDiv Event MouseEvent NodeList
自定义类型:
class | interface
数值、字符串let a:number=12;
let b:string = 'bmw’;
布尔值let isDone: boolean = false;
NewBoolean: boolean = new Boolean(1);返回对象
null undefined 同理
空值let unusable: void = undefined
function alertName(): void{}
注意:undefined 和 null 是所有类型的子类型,可以赋值给 number 类型的变量,而 void 类型的变量不能赋值给 number 类型的变量
任意值:any 允许被赋值为任意类型,任何操作都返回任意值
let myFavoriteNumber: any = 'seven';
myFavoriteNumber = 7;
类型推论没有明确的指定类型,依照值推断出一个类型。
let aa=12; //number 推论出为number类型
aa='qq';//无法修改
联合类型取值可以为多种类型中的一种,没列出的不可以
let myFavoriteNumber: string | number;
对象类型:依赖接口来描述,不给类型可以推论(原始数据类型和内置对象类型都没有object)
接口:interface Person {
name: string;
age: number;
}
let p:Person={name:'xx',age:11}
注意:定义的变量比接口少了一些属性是不允许
可选属性: age?: number;
任意属性: [propName: string]: any; 任意值
注意:必填属性和可选属性都必须是任意属性的子属性
例如:[propName: string]: string 其他属性要是string子属性
只读属性: readonly id: number; 只能创建的时候被赋值
数组:变量:类型[]:
let arr: number[] = [1, 1, 2, 3, 5];
let arr: any[] = [1, 1, 2, 3, 5];
Array<elemType>: 泛型 后面会单说
let arr: Array<number> = [1, 1, 2, 3, 5];
推荐:类型[ ] 这种方式;不然在tsx里有兼容问题
函数:一个函数有输入和输出,进行约束,需要把输入和输出都考虑到
function sum(x: number, y: number): number {}
注意:输入多余的(或者少于要求的)参数,是不被允许的
函数表达式:
let mySum = function (x: number, y: number): number {}
let mySum: (x: number, y: number) => number = function (x: number, y: number): number {}
变量:输入类型=>输出类型=function(参数){}
可选参数: function buildName(a: string, b?: string) {}
注意:可选参数在后
参数默认值: lastName: string = ‘Liu'
接口中函数的定义:
interface SearchFunc {
(a: string, b: number): boolean;
}
let c: SearchFunc=function() {return true}
c('qq',11)
类型断言Assertion:绕过编译器的类型推断,手动指定一个值的类型
<类型>值 (<string>something).length
值 as 类型 (something as string).length
注意:类型断言不是类型转换
在 tsx 语法(React 的 jsx 语法的 ts 版)中必须使用后者,即 值 as 类型。
用途:
将一个联合类型断言为其中一个类型
interface Cat {
name: string;
run(): void;
}
interface Fish {
name: string;
swim(): void;
}
function isFish(animal: Cat | Fish) {
if (typeof (animal as Fish).swim === 'function') {
return true;
}
return false;
}
需要注意的是,类型断言只能够「欺骗」TypeScript 编译器,无法避免运行时的错误,反而滥用类型断言可能会导致运行时错误:
将一个父类断言为更加具体的子类
class ApiError extends Error {
code: number = 0;
}
class HttpError extends Error {
statusCode: number = 200;
}
function isApiError(error: Error) {
if (typeof (error as ApiError).code === 'number') {
return true;
}
return false;
}
将任何一个类型断言为 any
但有的时候,我们非常确定这段代码不会出错,比如下面这个例子
window.foo = 1;
(window as any).foo = 1;
它极有可能掩盖了真正的类型错误,所以如果不是非常确定,就不要使用 as any
将 any 断言为一个具体的类型
在日常的开发中,我们不可避免地需要处理 any 类型的变量,它们可能是由于第三方库未能定义好自己的类型,也有可能是历史遗留的或其他人编写的烂代码,还可能是受到 TypeScript 类型系统的限制而无法精确定义类型的场景。
遇到 any 类型的变量时,我们可以选择无视它,任由它滋生更多的 any。
我们也可以选择改进它,通过类型断言及时地把 any 断言为精确的类型,亡羊补牢,使我们的代码向着高可维护性的目标发展。
function getCacheData(key: string): any {
return (window as any).cache[key];
}
interface Cat {
name: string;
run(): void;
}
const tom = getCacheData('tom') as Cat;
tom.run();
声明文件
ts 使用第三方库时,我们需要引用它的声明文件
ts 并不知道 $ 或 jQuery 是什么东西
declare 关键字来定义它的类型,帮助 TypeScript 判断我们传入的参数类型对不对
类型声明放到一个单独的文件中,这就是声明文件jQuery.d.ts
declare var jQuery: (string) => any;
jQuery('#p1');
declare var jQuery: (string) => any;
用到的文件的开头用「三斜线指令」表示引用了声明文件
/// <reference path="./jQuery.d.ts" />
安装第三方声明文件
npm install @types/jquery --save-dev
引入第三方方声明文件
import * as jQuery from 'jquery';
import * as $ from 'jquery';
用到的文件的开头用「三斜线指令」表示引用了声明文件
/// <reference path="./jQuery.d.ts" />
安装第三方声明文件
npm install @types/jquery --save-dev
引入第三方方声明文件
import * as jQuery from 'jquery';
import * as $ from 'jquery';
Boolean、Error、Date、RegExp,Math
let b: Boolean = new Boolean(1);大驼峰
Document、HTMLElement、Event、NodeList
let body: HTMLElement = document.body;
let allDiv: NodeList = document.querySelectorAll('p');
类(Class):定义了一件事物的抽象特点,包含它的属性和方法
对象(Object):类的实例,通过 new 生成
面向对象(OOP)的三大特性:封装、继承、多态
封装(Encapsulation):将对数据的操作细节隐藏起来,只暴露对外的接口。外界调用端不需要(也不可能)知道细节,就能通过对外提供的接口来访问该对象,同时也保证了外界无法任意更改对象内部的数据
实例属性: 定义在类内部 name = 'Jack'; | public xx:string 定义在构造器内部 | get|set 属性(){}
继承(Inheritance):子类继承父类,子类除了拥有父类的所有特性外,还有一些更具体的特性
es6: 使用 extends 关键字实现继承,子类中使用 super 关键字来调用父类的构造函数和方法
存取器(getter & setter):用以改变属性的读取和赋值行为
es6: 使用 get 属性(){return this._属性} 和 set 属性(val){this._属性=val} 可以改变属性的赋值和读取行为
静态方法: static 方法名(){} 类名.方法()
静态属性: static 定义在类内部 name = 'Jack'; ts实现了但转换到js暂不支持 调用:类名.方法()
访问修饰符(Modifiers):
修饰符是一些关键字,用于限定成员或类型的性质。比如 public 表示公有属性或方法
public:修饰的属性或方法是公有的,默认所有的属性和方法都是 public 的
private 修饰的属性或方法是私有的,不能在声明它的类的外部访问
protected 修饰的属性或方法是私有的 子类中允许访问
public name: string;
constructor(name: string)
sayHi(): void{}
p1: Person = new Person
可以用于对象的形状描述,函数的类型描述,类的行为进行抽象
思想:实现(implements)不同类之间可以有一些共有的特性,这时候就可以把特性提取成接口(interfaces),用 implements 关键字来实现
门是一个类,防盗门是门的子类。防盗门有一个报警器的功能,给防盗门添加一个报警方法。车内,也有报警器的功能,就可以考虑把报警器提取出来,作为一个接口,防盗门和车都去实现它
interface Action{ 定义接口
readonly id: number;//只读属性
name:string;
age?:number;可选
[propName: string]: any;//任意属性
eat?():string 可选方法的返回值
}
实现(implements)。一个类只能继承自另一个类,但是可以实现多个接口
泛型:在定义函数、接口或类的时候,不预先指定具体的类型,而在使用的时候再指定类型的一种特性
function createArray<T>(length: number, value: T): Array<T> {
let result: T[] = [];
for (let i = 0; i < length; i ) {
result[i] = value;
}
return result;
}
createArray<string>(3, 'x'); // ['x', 'x', 'x']
上例中,我们在函数名后添加了 <T>,其中 T 用来指代任意输入的类型,在后面的输入 value: T 和输出 Array<T> 中即可使用了。
接着在调用的时候,可以指定它具体的类型为 string。当然,也可以不手动指定,而让类型推论自动推算出来:
createArray(3, 'x'); // ['x', 'x', 'x’]
定义泛型的时候,可以一次定义多个类型参数:
function swap<T, U>(tuple: [T, U]): [U, T] {
return [tuple[1], tuple[0]];
}
swap([7, 'seven']); // ['seven', 7]
泛型约束:
interface Lengthwise {
length: number;
}
function loggingIdentity<T extends Lengthwise>(arg: T): T {
console.log(arg.length);
return arg;
}