深刻学习TypeScript

1、Typescript 介绍、环境搭建

1.1 Typescript 介绍

1.TypeScript 是由微软开发的一款开源的编程语言,像后端 java、C#这样的面向对象语言可让 js 开发大型企业项目。 javascript

2.TypeScript 是 Javascript的超级,遵循最新的 ES六、Es5 规范(至关于包含了es六、es5的语法)。TypeScript扩展了JavaScript的语法。 css

3.最新的 Vue 、React 也能够集成 TypeScript。html

1.2 Typescript 安装 编译

安装nodejs环境,用npm全局安装typescript
npm install -g typescript
Typescript文件后缀名为.ts,最后将编译成js文件

Typescript手动编译 => tsc + 文件名java

// 将index.ts编译成index.js
tsc index.ts

1.3 Typescript开发工具Vscode自动编译.ts 文件

1.3.1 tsc --init 生成配置文件tsconfig.json node

图片描述

1.3.2 点击菜单栏任务-运行任务(遇到错误使用快捷键ctrl + shift + b),点击 tsc:监视-tsconfig.json 而后就能够自动生成代码
git

图片描述

2、Typescript数据类型

typescript中为了使编写的代码更规范,更有利于维护,增长了类型校验es6

2.1 基础类型

在typescript中主要给咱们提供了如下数据类型:github

布尔类型(boolean)
数字类型(number)
字符串类型(string)
数组类型(array)
元组类型(tuple)
枚举类型(enum)
任意类型(any)
null和undefined
void类型
never类型

相比于js,typescript中多了枚举类型、任意类型、void类型和never类型ajax

2.2 变量定义

写ts代码变量必须指定类型,指定类型后赋值必须为指定的类型,不然报错算法

var flag:boolean = true
flag = 123 // 错误,类型不一致

2.3 数据类型

布尔类型(boolean)

var flag:boolean = true

flag = false // 正确

// flag=123;  // 错误

数字类型(number)

var num:number = 123;

num = 456; // 正确 

// num='str';    //错误

字符串类型(string)

var str:string = 'this is ts';

str='haha';  //正确

// str=true;  //错误

数组类型(array) ts中定义数组有两种方式

// 第一种
var arr:number[] = [1, 2, 3]

// 第二种
var arr2:Array<number> = [1, 2, 3]

元组类型(tuple)元素的类型没必要相同,写法和数组同样

let arr:[number,string] = [123,'this is ts']

枚举类型(enum)

用法:

enum 枚举名{ 
    标识符[=整型常数], 
    标识符[=整型常数], 
    ... 
    标识符[=整型常数], 
}
enum Flag {success = 1,error = 2};

let s:Flag = Flag.success // 使用枚举类型中的值
console.log('正确状态',s)
let f:Flag = Flag.error
console.log('错误状态',f)

任意类型(any)

为那些在编程阶段还不清楚类型的变量指定一个类型

var number:any = 123
number = 'str'
number = true

2.9 null 和 undefined

undefined:

{
    // 在js中,变量已声明但未初始化为undefined
    var undefinedTest:number
    // console.log(undefinedTest) // 错误写法,typescript报错,赋值了才正确

    // 在typescript中,已声明未初始化的值要直接访问的话类型须要定义为undefined
    var undefinedTest2:undefined
    console.log(undefinedTest2) // 正确写法,输出undefined 
}
{
    // 多是number类型 多是undefined
    var undefinedTest3:number | undefined;
    console.log(num);
}

null:

// null是一个空指针对象,undefined是未初始化的变量。所以,能够把undefined看做是空的变量,而null看做是空的对象
var nullTest:null
nullTest = null
// nullTest = {} // 错误,定义了类型是null,值必须为null

void类型

typescript中的void表示没有任何类型,通常用于定义方法的时候方法没有返回值。

// 表示方法没有返回任何类型
function run(): void {
    console.log('run')
}

run()

never类型

表示的是那些永不存在的值的类型,例如异常
var a:never

// a = 123 //错误写法
a = (() => {
    throw new Error('错误');
})()

3、Typescript函数

内容概述: 函数的定义、可选参数、默认参数、剩余参数、函数重载、箭头函数。

3.1.1 函数的定义

  • es5定义函数有函数声明法和匿名函数法
// 法一:函数声明法

function run():string {
    return 'run'
}

/**
// 错误写法
function run():string {
    return 123
}
*/

// 法二:匿名函数
var run2 = function ():string {
    return 'run2'
}

console.log('函数定义一', run())
console.log('函数定义二', run2())

3.1.2 ts中定义方法传参

  • 函数传参要指定数据类型
function paramFuc(name:string, age:number):string{
    return `${name} --- ${age}`
}

console.log('函数传参', paramFuc('dz', 20))

3.1.3 函数没有返回值的方法用void

function voidFnc():void{
    console.log('没有返回值的方法用void')
}
voidFnc();

3.2 可选参数

  • es5里面方法的实参和行参能够不同,可是ts中必须同样,若是不同就须要在可选参数后加?,这就是可选参数。
function electParam(name:string, age?:number):string {
    // 这里的age可传可不传,age就是可选参数
    if(age){
        return `${name} --- ${age}`
    }else{
        return `${name} --- 年龄保密`
    }
}
console.log('可选参数', electParam('dz'))

// 注意: 可选参数必须配置到参数的最后面

// 错误写法:可选参数不在最后面
// function electParam2(name?: string, age: number): string {
//     ...
// }

3.3 默认参数

  • es5里面无法设置默认参数,es6和ts中均可以设置默认参数
// age为默认参数
function defaultParam(name:string, age:number = 20):String {
    return `${name} --- ${age}`
}

console.log('默认参数', defaultParam('dz'))

3.4 剩余参数

  • 当有不少参数时候或参数个数不肯定,能够用三点运算符
// sum参数传过来的是一个数组
function sum(...result: number[]): number {
    var sum = 0;

    for (var i = 0; i < result.length; i++) {

        sum += result[i];
    }

    return sum;
}
console.log('剩余参数', sum(1, 2, 3, 4, 5, 6));

// a=1 b=2 其余参数为剩余参数
function sum2(a: number, b: number, ...result: number[]): number {
    var sum = a * b;

    for (var i = 0; i < result.length; i++) {

        sum += result[i];
    }

    return sum;
}
console.log('剩余参数2', sum2(1, 2, 3, 4, 5, 6));

3.5 ts函数重载

一样的函数,传入不一样的参数,实现不一样的功能
  • java中方法的重载:重载指的是两个或者两个以上同名函数,但它们的参数不同,这时会出现函数重载的状况。
  • typescript中的重载:经过为同一个函数提供多个函数类型定义来实现多种功能的目的。
  • ts为了兼容es5 以及 es6 重载的写法和java中有区别。
//  es5中同名函数,后面会覆盖前面的函数,ts中则不会 => 函数重载
function getInfo(name:string):string
function getInfo(name:string, age:number):string
function getInfo(name:any, age?:any):any {
    if(age) {
        return '姓名:' + name + '年龄:' + age
    }else{
        return '姓名:' + name
    }
}

console.log(getInfo('dz'))
console.log(getInfo('dz', 20))
// console.log(getInfo(20)) // 错误

3.6 箭头函数

箭头函数和es6中同样

setTimeout(() => {
    console.log('箭头函数')
}, 1000);

4、Typescript中的类

4.1 es5中的类

内容概述:类的建立、静态方法、继承(对象冒充继承,原型链继承,对象冒充 + 原型链组合继承)

es5中的面向对象、构造函数、原型与原型链本质能够看这个文档http://caibaojian.com/javascr... , 我的以为写得很清晰。

4.1.1 类的建立

es5类在构造函数和原型链里均可以添加属性和方法,原型链上的属性会被多个实例所共享,而构造函数则不会。

function Person() {
    this.name = 'Ming'
    this.run = function() {
        console.log(this.name + '在运动')
    }
}

Person.prototype.sex = '男' // 原型链上的属性会被多个实例所共享
Person.prototype.work = function() {
    console.log(this.name + '在工做')
}


var p = new Person()
p.run()
p.work()
console.log(p.name)

4.1.2 静态方法

调用静态方法不须要实例化
Person.getInfo=function(){
    console.log('我是静态方法');
}
Person.getInfo();

4.1.3 实现继承

对象冒充(或者叫构造函数继承)继承:能够继承构造函数里面的属性和方法,可是无法继承原型链上面的属性和方法

原型继承:能够继承构造函数里面的属性和方法,也能够继承原型链上面的属性和方法,可是实例化子类的时候无法给父类传参

下面是经过对象冒充 + 原型链组合继承,解决了上面两种继承方式存在的问题

function Worker(name,age){
    this.name=name;  /*属性*/
    this.age=age;
    this.run=function(){  /*实例方法*/
        alert(this.name+'在运动');
    }

}      
Worker.prototype.sex="男";
Worker.prototype.work=function(){
    alert(this.name+'在工做');
}
    
function Web(name,age){
    Worker.call(this,name,age);  // 对象冒充继承,能够继承构造函数里面的属性和方法,实例化子类能够给父类传参
}
// Web.prototype = new Worker();  // 原型链继承方法一:继承Worker构造函数和原型上全部的方法和属性
Web.prototype = Worker.prototype;  //原型链继承方法二:优化了方法一重复继承构造函数属性和方法的问题(本质能够看看http://caibaojian.com/javascript-object-5.html)

var w = new Web('赵四',20);   
w.run();
w.work();

从上面能够看出,对象冒充继承是在子类Web构造函数里面经过call方法继承父类Worker的构造函数的属性和方法;原型链继承经过子类Web的原型对象等于父类Worker的原型对象来实现继承;最后这两种继承的组合方式实现了完美继承。

4.2 typescript中的类

内容概述: ts中类的定义、继承、类修饰符、静态属性和静态方法、多态、抽象类和抽象方法

4.2.1 ts中类的定义

ts中类的定义和es6类的定义同样

class PersonDefine {
    name: string // 属性,前面省略了public关键词
    constructor(name:string) { //构造函数
        this.name = name
    }
    run():string { // 原型
        return `${this.name}在运动`
    }
}
var define = new PersonDefine('类的定义')
alert(define.run())

4.2.2 继承

ts中继承比es5简单不少,用extends super实现继承
class WebExtend extends PersonDefine {
    constructor(name:string) {
        super(name) // super继承父类的构造函数,并向父类构造函数传参
    }
    work():string {
        return `${this.name}在工做`
    }
}

var extend = new WebExtend('继承')
alert(extend.run())
alert(extend.work())

4.2.3 ts类里面的修饰符

修饰符:typescript里面定义属性的时候给咱们提供了三种修饰符

  • public: 公有修饰符,在当前类里面、子类、类外面均可以访问
  • protected:保护类型,在当前类里面、子类里面能够访问,在类外部无法访问
  • private :私有修饰符,在当前类里面能够访问,子类、类外部都无法访问

注意:属性若是不加修饰符,默认就是公有修饰符

// 以private为例
class PersonPrivate{
    private name:string;  /*被private修饰的属性 => 私有属性*/
    constructor(name:string){
        this.name=name;
    }
    run():string{
        return `${this.name}在运动` // 私有属性只能在当前类里面能够访问
    }
}

class Web extends PersonPrivate{
    constructor(name:string){
        super(name)
    }
    work(){
        // return `${this.name}在工做` // 报错,子类不能访问父类的私有属性
    }
}
 
var privateName = new PersonPrivate('private')
alert(privateName.run())
// console.log(privateName.name) // 报错,外部不能访问类的私有属性

4.2.4 静态属性和静态方法

为何要用静态属性和静态方法?jq里面的$.ajax就是用的静态方法
function $(element) {
    return new Base(element)
}

function Base(element) {
    this.element = document.getElementById(element)
    this.css = function(arr, value) {
        this.element.style[arr] = value
    }
}
$('box').css('color','red')
$.ajax = function() {}  // 想要在$上使用方法怎么办,用静态方法
ts中实现静态属性和静态方法用static
class PersonStatic{
    /*公有属性*/
    public name:string;
    constructor(name:string) {
        this.name=name;
    }
    /*实例方法(须要被实例化,因此为实例方法)*/
    run(){  
        return `${this.name}在运动`
    }
    /*静态属性*/
    static sex = '男'
    /*静态方法,里面无法直接调用类里面的属性*/
    static info(){  
        // return 'info方法' + this.name  // 静态方法不能调用本类的方法和属性,能够调用静态属性
        return 'info方法' + PersonStatic.sex
    }
}

console.log('静态方法' + PersonStatic.info())
console.log('静态属性' + PersonStatic.sex)

4.2.5 多态

父类定义一个方法不去实现,让继承它的子类去实现,每个子类的该方法有不一样的表现
  • 多态属于继承

好比定义一个父类Animal,里面的eat方法不去实现,让子类Dog和Cat分别实现本身的eat方法

class Animal {
    name:string;
    constructor(name:string) {
        this.name=name;
    }
    eat(){   // eat方法继承它的子类去实现
    }
}
class Dog extends Animal{
    constructor(name:string){
        super(name)
    }
    eat(){
        return this.name+'吃粮食'
    }
}

class Cat extends Animal{
    constructor(name:string){
        super(name)
    }
    eat(){
        return this.name+'吃老鼠'
    }
}

4.2.6 抽象类和抽象方法

定义:用abstract关键字定义抽象类和抽象方法,抽象类中的抽象方法不包含具体实现而且必须在派生类(抽象类的子类)中实现
  • 抽象类:它是提供其余类继承的基类,不能直接被实例化,子类继承能够被实例化
  • abstract修饰的方法(抽象方法)只能放在抽象类里面
  • 抽象类和抽象方法用来定义标准(好比定义标准为:抽象类Animal有抽象方法eat,要求它的子类必须包含eat方法)
abstract class AnimalAbst{
    public name:string;
    constructor(name:string){
        this.name=name;
    }
    abstract eat():any;  //抽象方法不包含具体实现而且必须在派生类中实现
    run(){
        console.log('其余方法能够不实现')
    }
}
// var a = new Animal() /*错误的写法,抽象类不能被实例化*/

class DogAbst extends Animal{
    //抽象类的子类必须实现抽象类里面的抽象方法
    constructor(name:any){
        super(name)
    }
    eat(){
        return this.name + '吃粮食'
    }
}

var d = new DogAbst('小花花');
console.log('抽象类和抽象方法',d.eat());

5、TypesSript接口

接口定义:接口是对传入参数进行约束;或者对类里面的属性和方法进行声明和约束,实现这个接口的类必须实现该接口里面属性和方法;typescript中的接口用interface关键字定义。

接口做用:接口定义了某一批类所须要遵照的规范,接口不关心这些类的内部状态数据,也不关心这些类里方法的实现细节,它只规定这批类里必须提供某些方法,提供这些方法的类就能够知足实际须要。typescrip中的接口相似于java,同时还增长了更灵活的接口类型,包括属性、函数、可索引和类等。

内容概述:接口分类:(属性接口、函数类型接口、可索引接口、类类型接口),接口的继承

5.1 接口分类

5.1.1 属性接口

对传入对象的约束(也就是对json的约束)

在了解接口以前,咱们来看看函数传入obj参数

function printLabel(labelInfo: {label:string}){
    return labelInfo
}
// printLabel({name:'obj'});  //错误的写法
console.log(printLabel({label: 'obj'}))

和上面相似,由此引入属性接口 => 对方法传入参数进行约束

下面为属性接口的例子,方法printFullName对传入参数FullName(为对象)进行约束

interface FullName{
    firstName: string; // 注意;结束
    secondName: string;
    age?: number // 接口的可选属性用?
}

function printFullName(name:FullName) {
    // 传入对象必须包含firstName和secondName,可传可不传age
    return name
}
var obj = {
    firstName:'小',
    secondName:'明',
    age: 20
}
console.log(printFullName(obj))

属性接口应用:原生js封装ajax

interface Config{
    type: string;
    url: string;
    data?: string;
    dataType: string;
}
function ajax(config: Config) {
    var xhr = new XMLHttpRequest
    xhr.open(config.type, config.url, true)
    xhr.send(config.data)
    xhr.onreadystatechange = function() {
        if(xhr.readyState == 4 && xhr.status == 200) {
            if(config.dataType == 'json'){
                console.log(JSON.parse(xhr.responseText))
            }else{
                console.log(xhr.responseText)
            }
        }
    }
}

ajax({
    type: 'get',
    data: 'name=xiaoming',
    url: 'http://a.itying.com/api/productlist',
    dataType: 'json'
})

5.1.2 函数类型接口

对方法传入的参数以及返回值进行约束
interface encrypt{
    (key: string, value: string): string; // 传入的参数和返回值的类型
}

var md5:encrypt = function(key:string, value:string):string{
    // encrypt对加密方法md5进行约束,同时md5方法的参数和返回值类型和encrypt要保持一致
    return key + value
}

console.log(md5('name', '小明'))

5.1.3 可索引接口

对索引和传入参数的约束(通常用于对数组、对象的约束)

ts中定义数组:

var arr1:number[] = [1,2]
var arr2:Array<string> = ['1', '2']

如今用接口来实现:

// 对数组的的约束
interface UserArr{
    // 索引为number,参数为string
    [index:number]: string
}
var userarr:UserArr = ['a', 'b']
console.log(userarr)
// 对象的约束
interface UserObj{
    // 索引为string,参数为string
    [index:string]: string
}
var userobj:UserObj = { name: '小明', sex: '男' }
console.log(userobj)

5.1.4 类类型接口

对类的约束,和抽象类抽象有点类似
interface Animal{
    // 对类里面的属性和方法进行约束
    name:string;
    eat(str:string):void;
}
// 类实现接口要用implements关键字,必须实现接口里面声明的方法和属性
class Cat implements Animal{
    name:string;
    constructor(name:string){
        this.name = name
    }
    eat(food:string){
        console.log(this.name + '吃' + food)
    }
}
var cat = new Cat('小花')
cat.eat('老鼠')

5.2 接口的继承

和类的继承同样,用extends实现接口继承

下面同时实现类的继承和接口的继承

interface Animal {
    eat(): void;
}
// 继承Animal接口,则实现Person接口的类必须也实现Animal接口里面的方法
interface Person extends Animal {
    work(): void;
}

class Programmer {
    public name: string;
    constructor(name: string) {
        this.name = name;
    }
    coding(code: string) {
        console.log(this.name + code)
    }
}

// 继承类而且实现接口
class Web extends Programmer implements Person {
    constructor(name: string) {
        super(name)
    }
    eat() {
        console.log(this.name + '吃')
    }
    work() {
        console.log(this.name + '工做');
    }
}

var w = new Web('小李');
w.eat();
w.coding('写ts代码');

6、TypesSript泛型

泛型定义:泛型定义:泛型就是解决类、接口、方法的复用性,以及对不特定数据类型的支持(类型校验)。ts中用T表示泛型。

泛型公式: <T>表示泛型,调用的时候指定T的数据类型

软件工程中,咱们不只要建立一致的定义良好的API,同时也要考虑可重用性。 组件不只可以支持当前的数据类型,同时也能支持将来的数据类型,这在建立大型系统时为你提供了十分灵活的功能。

在像C#和Java这样的语言中,可使用泛型来建立可重用的组件,一个组件能够支持多种类型的数据。 这样用户就能够以本身的数据类型来使用组件。

内容概述:内容概述:函数的泛型、类的泛型、泛型接口

6.1 函数的泛型

传入的参数类型和返回的参数类型能够指定

咱们来看看函数用ts数据类型,想要同时返回string类型和number类型

function getData1(value:string):string{
    return value;
}
function getData2(value:number):number{
    return value;
}

这样要写不一样的函数,不能按照需求返回不一样类型数据,形成代码冗余 => 由此引入泛型

<T>表示泛型,调用的时候指定T的数据类型
function dataT<T>(value:T):T{
    // 传入参数为T 返回值为T
    return value
}
dataT<number>(1) // 调用指定泛型为number类型,则传入参数也必须为number类型
dataT<string>('string')

function dataAny<T>(value:T):any{
    return '传入参数为T,任意类型返回值';
}
dataAny<number>(123); // 参数必须是number
dataAny<string>('这是一个泛型');

6.2 类的泛型

也是用<T>来实现类的泛型,new的时候指定T的数据类型

有个最小堆算法,须要同时支持返回数字和字符串两种类型

使用泛型以前:只能在类的类部指定数据类型,实现需求还要写一套string类型的类

class MinClass{
    public list:number[]=[];
    add(num:number){
        this.list.push(num)
    }
    min():number{
        var minNum=this.list[0];
        for(var i=0;i<this.list.length;i++){
            if(minNum>this.list[i]){
                minNum=this.list[i];
            }
        }
        return minNum;
    }
}

var m=new MinClass();
m.add(1);
m.add(2);
alert(m.min());

使用泛型以后:只用一套类来实现

class MinClassT<T>{
    public list:T[]=[];
    add(value:T):void{
        this.list.push(value);
    }
    min():T{        
        var minNum=this.list[0];
        for(var i=0;i<this.list.length;i++){
            if(minNum>this.list[i]){
                minNum=this.list[i];
            }
        }
        return minNum;
    }
}
var m1=new MinClassT<number>();   /*实例化类 而且指定了类的T表明的类型是number*/
m.add(1);
m.add(2);
alert(m1.min())

var m2=new MinClassT<string>();   /*实例化类 而且指定了类的T表明的类型是string*/
m2.add('c');
m2.add('a');
alert(m2.min())

6.3 泛型接口

有一个函数类型接口

interface ConfigFn{
    (value:string):string;
}
var setData:ConfigFn = function(value:string):string{
    return value
}
setData('name');
// setData(20); // 错误

setData(20);写法错误,想要传入number类型的参数又要写一个函数类型接口 => 用泛型接口

泛型接口有两种写法:

// 泛型接口定义方式一
interface ConfigFnOne{
    <T>(value:T):T;
}
var setDataOne:ConfigFnOne = function<T>(value:T):T{
    return value
}
// 既能够传入string也能够传入number类型参数
setDataOne<string>('name');
setDataOne<number>(20);
// 泛型接口定义方式二
interface ConfigFnTwo<T>{
    (value:T):T;
}
function setDataTwo<T>(value:T):T{
    return value
}
var setDataTwoFn:ConfigFnTwo<string> = setDataTwo
setDataTwoFn('name');

示例代码请查看github,欢迎start https://github.com/dzfrontend...

相关文章
相关标签/搜索