一文带你了解typeScript

typeScript

分章节阅读目录链接:typeScript系列学习文章目录

  • typeScript数据类型
    • 解释
  • typeScript函数
    • 函数定义
    • 参数
    • 函数重载
    • Lambda函数
  • typeScript类
    • 类的定义
    • 继承
    • 类里面的修饰符
    • 静态属性静态方法
    • 多态
    • 抽象类
  • typeScript接口
    • 属性接口
    • 函数类型接口
    • 可索引接口
    • 类类型接口
    • 接口扩展
  • typeScript中的泛型
    • 泛型的定义
    • 泛型函数
    • 泛型类
    • 泛型接口
  • 模块
    • 模块的的概念
    • 模块导出的几种方法
    • 命名空间
  • 装饰器
    • 类装饰器
    • 属性装饰器
    • 方法装饰器
    • 参数装饰器
  • 声明文件

typeScript数据类型

布尔类型(boolean)

数字类型(number)

字符串类型(string)

数组类型(array)

元组类型(tuple)

枚举类型(enum)

任意类型(any)

null 和 undefined

void类型

never类型

解释

字符串类型(string)

var str:string = "fur"
str = "furfur"
console.log(str)//furfur

数组类型(array)

//写法一
var arr:number[] = [1,2,3]
console.log(arr)//[ 1, 2, 3 ]

//写法二
var arr:Array<number> = [1,2,3]
console.log(arr)//[ 1, 2, 3 ]

**元组类型(tuple)**属于数组的一种。

var arr:[string,number] = ["fur",1]
console.log(arr)//[ "fur", 1 ]

枚举类型(enum)

枚举方法:事先考虑到某一变量可能取的值,尽量用自然语言中含义清楚的单词来表示它的每一个值,用这种方法定义的类型称枚举类型。(就是起一个通俗易懂的别名)

enum 枚举名{ 
                标识符[=整型常数], 
                标识符[=整型常数], 
                ... 
                标识符[=整型常数], 
            } ;
enum Flag{
    success = 1,
    error = 0,
}
let s:Flag = Flag.success
console.log(s)//1

任意类型(any):相当于没有类型限制

var num:any=123;
num='str';
num=true;
console.log(num)

null 和 undefined是其他(never类型)数据类型的子类型

var num1:number
var num2:undefined
var num3:null=null
console.log(num1)//编译器报错
console.log(num2)//undefined   
console.log(num3)//null

void类型:表示没有任何类型,一般用于定义方法的时候方法没有返回值。

function play():void{
    console.log("fur")
}
play()

never类型:表示的是那些永不存在的值的类型。

never类型是任何类型的子类型,也可以赋值给任何类型;然而,没有类型是never的子类型或可以赋值给never类型(除了never本身之外)。 即使 any也不可以赋值给never。通常表现为抛出异常或无法执行到终止点(例如无线循环)。

let x: never;
let y: number;

// 编译错误,数字类型不能转为 never 类型
x = 123;

// 运行正确,never 类型可以赋值给 never类型
x = (()=>{ throw new Error('exception')})();

// 运行正确,never 类型可以赋值给 数字类型
y = (()=>{ throw new Error('exception')})();

// 返回值为 never 的函数可以是抛出异常的情况
function error(message: string): never {
    throw new Error(message);
}

// 返回值为 never 的函数可以是无限循环这种无法被执行到的终止点的情况
function loop(): never {
    while (true) {}
}

上述never部分的理解参考自文章:https://segmentfault.com/a/1190000008893626

联合类型

联合类型(Union Types)可以通过管道(|)将变量设置多种类型,赋值时可以根据设置的类型来赋值。

var val:string|number 
val = 1
console.log(typeof(val)) //number
val = "fur" 
console.log(typeof(val)) //string

typeScript函数

函数的定义

可选参数,默认参数,剩余参数

函数重载

箭头函数 es6

函数定义

函数就是包裹在花括号中的代码块,前面使用了关键词 function:

语法格式如下所示:

function run():void{
    // 执行代码
}

var run = function ():void{
    // 执行代码
}

参数

方法传参

var run = function (name:string):string{
    // 执行代码
    return`${name}在跑步`
}
console.log(run("fur"))//fur在跑步

可选参数

es5里面方法的实参和行参可以不一样,但是ts中必须一样,如果不一样就需要配置可选参数

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

var run = function (name:string,sex?:string):string{
    // 执行代码
    if(sex){
        return`${name}在跑步---${sex}`
    }else{
        return`${name}在跑步`
    }
}
console.log(run("fur"))//fur在跑步
console.log(run("fur","女"))//fur在跑步---女

默认参数

es5里面没法设置默认参数,es6和ts中都可以设置默认参数

var run = function (name:string,sex:string="女"):string{
    // 执行代码
    if(sex){
        return`${name}在跑步---${sex}`
    }else{
        return`${name}在跑步`
    }
}
console.log(run("fur"))//fur在跑步---女
console.log(run("fur","男"))//fur在跑步---男

剩余参数

...三点运算符,接受新参传过来的值

function sum(...arr:number[]):number{
    var item = 0;
    var sum = 0;
    for(item of arr){
        sum+=item
    }
    return sum;
}
console.log(sum(1,2,3,4,5,6))//21
console.log(sum(1,2,3))//6

函数重载

java中方法的重载:重载指的是两个或者两个以上同名函数,但它们的参数不一样,这时会出现函数重载的情况。

typescript中的重载:通过为同一个函数提供多个函数类型定义来试下多种功能的目的。

注意:**必须要把精确的定义放在前面,最后函数实现时,需要使用 |操作符或者?操作符,把所有可能的输入类型全部包含进去,以具体实现。**即最下面的方法需要兼容上面的方法

function run(num:number):void;
function run(str:string,flag:boolean):void;
function run(str:string,flag?:boolean):void;
function run(str:number|string,flag?:boolean):void{
    if(typeof str === 'string'){
        console.log("fur")
    }else{
        console.log(1)
    }
}
run(1)
run("fur")
run("fur",true);

常见报错:This overload signature is not compatible with its implementation signature

意思:此重载签名与其实现签名不兼容,即没有满足最后的函数实现时,把所有可能输入的函数全部包含进去。

Lambda 函数

Lambda 函数也称之为箭头函数。箭头函数表达式的语法比函数表达式更短。

( [param1, parma2,…param n] )=>statement;

最简写法

var result = (num:number)=>num+1
console.log(result(2))//3

正常写法

var result = (num:number)=>{
    //执行代码
    console.log(num+1)
}
result(2)//3

typeScript 类

类描述了所创建的对象共同的属性和方法。

类的定义

继承

类里面的修饰符

静态属性 静态方法

抽象类 继承 多态

类的定义

class person {
    name:string;  //属性,省略public
    constructor(n:string){
        this.name = n
    }
    run():void{
        console.log(this.name+"在跑步")
    }
}
var p = new person("fur");
console.log(p.name)//fur
p.run()//fur在跑步

继承

关键词:extends super

run方法说明:子类继承父类方法

study方法说明:子类重写父类方法

class person {
    name:string;  //属性,省略public
    constructor(n:string){
        this.name = n
    }
    run():void{
        console.log(this.name+"在跑步")
    }
    study():void{
        console.log(this.name+"在学习")
    }
}
class man extends person{
    constructor(name:string){
        super(name) /*初始化父类的构造函数*/
    }
    study():void{
        console.log(this.name+"在学习-子类")
    }
}
var p = new person("fur");
console.log(p.name)//fur
p.run()//fur在跑步
var m = new man("furfur");

console.log(m.name)//furfur
m.run()//furfur在跑步(继承父类的run)
m.study()//furfur在学习-子类(重写父类study)

对比es5的继承有很大的改善,es5继承

类里面的修饰符

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

  • public : 公有,在当前类里面、 子类 、类外面都可以访问

  • protected:保护类型,在当前类里面、子类里面可以访问 ,在类外部没法访问

  • private :私有,在当前类里面可以访问,子类、类外部都没法访问

例子:

class person {
    private name:string;  
    constructor(n:string){
        this.name = n
    }
    protected run():void{
        console.log(this.name+"在跑步")//正确,private只能在类“person”中访问
    }
    public study():void{
        console.log(this.name+"在学习")
    }
    
}
class man extends person{
    constructor(name:string){
        super(name) /*初始化父类的构造函数*/   
    }
    protected run():void{
        super.run() // 正确,protected只能在类“person”及其子类中访问。
    }
}
var p = new person("fur");
// console.log(p.name)//报错:属性“name”为私有属性,只能在类“person”中访问。
// p.run()//报错:属性“run”受保护protected,只能在类“person”及其子类中访问。
p.study()//furfur在学习


var m = new man("furfur");
// console.log(m.name)//报错:属性“name”为私有属性,只能在类“person”中访问。
// m.run()//报错:属性“run”受保护protected,只能在类“man”及其子类中访问。
m.study()//furfur在学习

静态属性 静态方法

关键词:static

class person {
     name:string; 
     static age = 1
    constructor(n:string){
        this.name = n
    }
    // static run():void{
    //     console.log(this.name+"在跑步")//静态方法  里面没法直接调用类里面的非静态属性
    // }
    static getAge():void{
        console.log(this.age)
    }   
}
person.age=1
person.getAge()

多态

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

例子:person中的paly方法,子类man和woman有各自的具体方法

class person {
    name:string; 
    age = 1
    constructor(n:string){
        this.name = n
    }
    play():void{
        console.log("玩法")
    }
}
class man extends person{
    constructor(name:string){
        super(name)
    }
    play():void{
        console.log(`${this.name} lol`)
    }
}
class woman extends person{
    constructor(name:string){
        super(name)
    }
    play():void{
        console.log(`${this.name} shopping`)
    }
}
var m = new man("boy")
m.play()//boy lol
var w = new woman("girl")
w.play()//girlshopping

抽象类

typescript中的抽象类:抽象类和抽象方法用来定义标准 。它是提供其他类继承的基类,不能直接被实例化。

用abstract关键字定义抽象类和抽象方法,抽象类中的抽象方法不包含具体实现并且必须在派生类中实现。abstract抽象方法只能放在抽象类里面。

抽象类的子类必须实现抽象类里面的抽象方法,否则仍然要作为一个抽象类。

abstract class person {
    name:string; 
    age = 1
    constructor(n:string){
        this.name = n
    }
    abstract play():void;
}
class man extends person{
    constructor(name:string){
        super(name)
    }
    play():void{
        console.log(`${this.name} lol`)
    }
}
class woman extends person{
    constructor(name:string){
        super(name)
    }
    play():void{
        console.log(`${this.name} shopping`)
    }
}
var m = new man("boy")
m.play()//boy lol
var w = new woman("girl")
w.play()//girlshopping

typeScript接口

属性类接口

函数类型接口

可索引接口

类类型接口

接口扩展

接口是一系列抽象方法的声明,是一些方法特征的集合,这些方法都应该是抽象的,需要由具体的类去实现,然后第三方就可以通过这组抽象方法调用,让具体的类执行具体的方法。

接口的作用:在面向对象的编程中,接口是一种规范的定义,它定义了行为和动作的规范,在程序设计里面,接口起到一种限制和规范的作用。

接口定义了某一批类所需要遵守的规范,接口不关心这些类的内部状态数据,也不关心这些类里方法的实现细节,它只规定这批类里必须提供某些方法,提供这些方法的类就可以满足实际需要。

typescrip中的接口类似于java,同时还增加了更灵活的接口类型,包括属性、函数、可索引和类等。

接口:行为和动作的规范,对批量方法进行约束

属性接口

对json的约束

interface Person {
    name:string;
    age:number;
    sex?:string//可选属性
}
function printPerson (p:Person){
    if(p.sex){
        console.log(`${p.name}年龄${p.age} 性别${p.sex}`)
    }else{
        console.log(`${p.name}年龄${p.age}`)

    }
}

//写法一
printPerson({name:"fur",age:1})//fur年龄1
printPerson({name:"fur",age:1,sex:"girl"})//fur年龄1 性别girl
//写法二
var obj:Person = {
    name:"furfur",
    age:2,
    sex:"girl"
}
printPerson(obj)//furfur年龄2 性别girlrfur年龄2 性别girl

函数类型接口

对方法传入的参数 以及返回值进行约束,批量约束

interface encrypt{
    (key:string,value:string):string;
}
var md5:encrypt=function(key:string,value:string):string{
        //模拟操作
        return key+value;
}
console.log(md5('name','fur'));//namefur

可索引接口

数组、对象的约束

//对数组的约束
interface Arr  {
[index:number]:string;
}
var arr1:Arr = ["1","2","3"]

//对对象的约束
interface Obj {
    [index:string]:string
}
var arr2:Obj = {name:"fur",age:"1"}

类类型接口

对类的约束和抽象类抽象有点相似

class person {
    name:string; 
    constructor(n:string){
        this.name = n
    }
    play():void{
    }
}
class man implements person{
    name: string;
    constructor(name:string){
        this.name = name;
    }
    play():void{
        console.log(`${this.name} lol`)
    }
}
class woman implements person{
    name: string;
    constructor(name:string){
        this.name = name;
    }
    play():void{
        console.log(`${this.name} shopping`)
    }
}
var m = new man("boy")
m.play()//boy lol
var w = new woman("girl")
w.play()//girlshopping

接口扩展

接口可以继承接口

例子:接口person继承接口Animal,man实现接口person同时也要实现Animal

class Animal{
    eat():void{
    }
}
class person extends Animal{
    play():void{
    }
}

class man implements person{
    eat():void {
        console.log('吃粮食')
    }
    play():void{
        console.log('玩游戏')
    }
}

typeScript中的泛型

泛型的定义

泛型函数

泛型类

泛型接口

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

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

通俗理解:泛型就是解决 类 接口 方法的复用性、以及对不特定数据类型的支持(类型校验)

泛型的定义

泛型:可以支持不特定的数据类型, 要求:传入的参数和返回的参数一致

可以用T表示泛型,具体什么类型是调用这个方法的时候决定的

泛型函数

function getData<T>(val:T):T{
    return val
}
console.log(getData<number>(1))//1
console.log(getData<string>("fur"))//fur
console.log(getData<boolean>(true))//true

泛型类

class MinClass<T>{
    public list:T[] = []
    add(val:T):void{
        this.list.push(val)
    }
    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 mNum = new MinClass<number>();
mNum.add(8);
mNum.add(6);
mNum.add(11);
console.log(mNum.min())//6
var mStr = new MinClass<string>();
mStr.add("fur")
mStr.add("kfc")
mStr.add("mdl")
console.log(mStr.min())//fur

泛型接口

interface config<T> {
    (value:T):T
}
function getData<T>(val:T):T{
    return val;
}
var myData:config<string> = getData; 
console.log(myData("fur"))//fur

模块

模块的的概念

“外部模块”现在则简称为“模块” 模块在其自身的作用域里执行,而不是在全局作用域里;

“内部模块”现在称做“命名空间”。

​ 这意味着定义在一个模块里的变量,函数,类等等在模块外部是不可见的,除非你明确地使用export形式之一导出它们。

​ 相反,如果想使用其它模块导出的变量,函数,类,接口等的时候,你必须要导入它们,可以使用 import形式之一。

模块导出的几种方法

由于浏览器端无法解析export,可以使用node运行或者用webpack打包工具打包。

导入导出方法一

export导出:

var url='xxx';
function getData():void{
    console.log('fur');
}
export {url,getData};

import导入:

import {url,getData as get } from './modules/db'   
//as可以更换名字,地址是export的文件的地址
console.log(url)//xxx
get()//fur

导入导出方法二

导出方法:export default 默认导出

每个模块都可以有一个default导出。 默认导出使用 default关键字标记;并且一个模块只能够有一个default导出。 需要使用一种特殊的导入形式来导入 default导出。

export导出:

function getData():void{
    console.log('fur');
}
export default getData;

import导入:

import getData from './modules/db';
getData();//fur

命名空间

在代码量较大的情况下,为了避免各种变量命名相冲突,可将相似功能的函数、类、接口等放置到命名空间内,typeScript的命名空间可以将代码包裹起来,只对外暴露需要在外部访问的对象。

命名空间内的对象通过export关键字对外暴露。

命名空间和模块的区别:

  • 命名空间:内部模块,主要用于组织代码,避免命名冲突。

  • 模 块:ts的外部模块的简称,侧重代码的复用,一个模块里可能会有多个命名空间。

命名空间使用 namespace 来定义:

空间A和B内代码一模一样,可以共存,调用时还需要命名空间的名字调用

namespace A{
    interface Person {
    name:string;
    paly():void;
    }
    export class man implements Person {
        name: string;
        constructor(n: string) {
            this.name = n;
        }
        paly():void{
            console.log("玩游戏")
        }
    }
}
namespace B{
    interface Person {
    name:string;
    paly():void;
    }
    export class man implements Person {
        name: string;
        constructor(n: string) {
            this.name = n;
        }
        paly():void{
            console.log("跑步")
        }
    }
}
var p1 = new A.man("fur")
p1.paly()//玩游戏
var p2 = new B.man("fur")
p2.paly()//跑步

装饰器

装饰器:装饰器是一种特殊类型的声明,它能够被附加到类声明,方法,属性或参数上,可以修改类的行为。

通俗的讲装饰器就是一个方法,可以注入到类、方法、属性参数上来扩展类、属性、方法、参数的功能。

常见的装饰器有:类装饰器、属性装饰器、方法装饰器、参数装饰器

装饰器的写法:普通装饰器(无法传参) 、 装饰器工厂(可传参)

注意:使用装饰器会报以下错误,点击快速修复或者手动在json文件里增加以下代码

"experimentalDecorators": true,

对修饰器的实验支持是一项将在将来版本中更改的功能。设置 "experimentalDecorators" 选项以删除此警告。

运行顺序:属性>方法>方法参数>类 如果有多个同样的装饰器,它会先执行后面的

类装饰器

类装饰器在类声明之前被声明(紧靠着类声明)。 类装饰器应用于类构造函数,可以用来监视,修改或替换类定义。

类装饰器:普通装饰器(无法传参)

function logClass(params:any){
    console.log(params)
    // 动态扩展属性eg
    params.prototype.name = "fur"
    //动态扩展方法
    params.prototype.run =function(){
        console.log("run")
    }
}
@logClass
class  Person { 
    study(){
        console.log("study")
    }
}

var p:any = new Person()
p.study()//study
console.log(p.name)//fur
p.run()//run

类装饰器:装饰器工厂(可传参)

function logClass(params:any){
    return function(target:any){
        target.prototype.name=params;
    }
}
@logClass('furfur')
class  Person { 
    study(){
        console.log("study")
    }
}
var p:any = new Person()
p.study()//study
console.log(p.name)//furfur

属性装饰器

属性装饰器表达式会在运行时当作函数被调用,传入下列2个参数:

​ 1、对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。

​ 2、成员的名字。

function logProperty(params:any){
    return function(target:any,attr:any){
        target[attr]=params;
        console.log(attr)
        console.log(target)
    }
}

class  Person {
    @logProperty("fur") 
    name:string|undefined;
    constructor(){
    }
    study(){
        console.log("study")
    }
}

var p:any = new Person()
p.study()//study
console.log(p.name)//fur

方法装饰器

它会被应用到方法的 属性描述符上,可以用来监视,修改或者替换方法定义。

方法装饰会在运行时传入下列3个参数:

​ 1、对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。

​ 2、成员的名字。

​ 3、成员的属性描述符

function get(params:any){
    return function(target:any,methodName:any,desc:any){
        console.log(methodName);//getData
        target.apiUrl='fur';
        target.run=function(){
            console.log('run');
        }
    }
}

class HttpClient{  
    public url:any |undefined;
    constructor(){
    }
    @get('http://www.fur.com')
    getData(){
        console.log(this.url);
    }
}

var http:any=new HttpClient();
console.log(http.apiUrl);//fur
http.run();//run

方法参数装饰器

方法参数装饰器表达式会在运行时当作函数被调用,传入下列3个参数:

​ 1、对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。

​ 2、参数的名字。

​ 3、参数在函数参数列表中的索引。

function logParams(params:any){
    return function(target:any,methodName:any,paramsIndex:any){
        console.log(params);//fur
        console.log(target);//object
        console.log(methodName);//getData
        console.log(paramsIndex);//0
        target.apiUrl=params;
    }   
}

class HttpClient{  
    public url:any |undefined;
    constructor(){
    }           
    getData(@logParams('fur') uuid:any){               
        console.log(uuid);
    }
 }

var http:any = new HttpClient();
http.getData("furfur");//furfur
console.log( http.apiUrl);//fur

声明文件

在开发过程中不可避免要引用其他第三方的 JavaScript 的库。虽然通过直接引用可以调用库的类和方法,但是却无法使用TypeScript 诸如类型检查等特性功能。

为了解决这个问题,需要将这些库里的函数和方法体去掉后只保留导出类型声明,而产生了一个描述 JavaScript 库和模块信息的声明文件。通过引用这个声明文件,就可以借用 TypeScript 的各种特性来使用库文件了。

假如我们想使用第三方库,比如 jQuery,我们通常这样获取一个 id 是 foo 的元素:

$('#foo');
// 或
jQuery('#foo');

但是在 TypeScript 中,我们并不知道 $ 或 jQuery 是什么东西:

jQuery('#foo');

// index.ts(1,1): error TS2304: Cannot find name 'jQuery'.

这时,我们需要使用 declare 关键字来定义它的类型,帮助 TypeScript 判断我们传入的参数类型对不对:

declare var jQuery: (selector: string) => any;

jQuery('#foo');

declare 定义的类型只会用于编译时的检查,编译结果中会被删除。

上例的编译结果是:

jQuery('#foo');

声明文件

声明文件以 .d.ts 为后缀;

fur.d.ts

声明文件或模块的语法格式如下:

declare module Module_Name {
}

TypeScript 引入声明文件语法格式:

/// 

当然,很多流行的第三方库的声明文件不需要我们定义了,比如 jQuery 已经有人帮我们定义好了:jQuery in DefinitelyTyped。

实例

以下定义一个第三方库来演示:

CalcThirdPartyJsLib.js 文件代码:

var Runoob;   
(function(Runoob) {    
    var Calc = (function () {         
        function Calc() {         
        }     
    })    
    Calc.prototype.doSum = function (limit) {        
        var sum = 0;          
        for (var i = 0; i <= limit; i++) {            
            sum = sum + i;         
        }        
        return sum;     
    }    
    Runoob.Calc = Calc;     
    return Calc;  
})(Runoob || (Runoob = {}));  
var test = new Runoob.Calc();

如果我们想在 TypeScript 中引用上面的代码,则需要设置声明文件 Calc.d.ts,代码如下:

Calc.d.ts 文件代码:

declare module Runoob {    
    export class Calc {       
        doSum(limit:number) : number;    
    } 
}

声明文件不包含实现,它只是类型声明,把声明文件加入到 TypeScript 中:

CalcTest.ts 文件代码:

///   
var obj = new Runoob.Calc();  
// obj.doSum("Hello"); // 编译错误 
console.log(obj.doSum(10));

生成的 JavaScript 代码如下:

CalcTest.js 文件代码:

///   
var obj = new Runoob.Calc(); 
//obj.doSum("Hello"); // 编译错误 
console.log(obj.doSum(10));

最后我们编写一个 runoob.html 文件,引入 CalcTest.js 文件及第三方库 CalcThirdPartyJsLib.js:

浏览器打开该文件输出结果如下:

一文带你了解typeScript_第1张图片

声明文件这一篇来自菜鸟教程

一文带你了解typeScript_第2张图片

你可能感兴趣的:(TypeScript)