美文网首页
TypeScript超详细教程

TypeScript超详细教程

作者: 十年之后_b94a | 来源:发表于2020-12-23 13:50 被阅读0次

    前言

    TypeScriptJavaScript 的一个超集,支持 ECMAScript 6 标准
    可以理解成TypeScript是高版本的JS
    中文学习网址

    安装TypeScript

    npm install -g typescript
    

    编写第一个TS

    注意此时文件的后缀不再是js而是ts

    function hello(){
      let str:string = "Hello Word!!";
      console.log(str)
    }
    hello();
    

    然后执行命令

    tsc ***.ts
    

    执行结束后 会转为js文件 然后我们在执行js文件
    这一系列操作下来太麻烦了,我们直接一步到位

    安装便捷编译

    npm i ts-node -g
    

    执行当前命令会直接输出

    ts-node ***.ts
    

    定义变量

    在ts中一旦定义了一个变量可以指定一个类型。
    基础静态类型:
    number、string、void、null、undefined、boolean、symbol、、、
    一旦定义了类型,那么在重新赋值必须是和第一次赋值一样的类型,即使你在定义变量的时候不指定类型

    let a : number = 1;
    let b : string = "1";
    b = 1;//报错字符串数字和number类型数字也是不能直接赋值的
    let c = false;//在ts中这也是可法的,在定义的时候你可以不用指定类型
    c = "false";//报错因为当前c变量在第一次赋值是属于boolean类型你不可改变该类型
    c = true;//可法
    c = 0;//不可法
    c = !0;//可法,你可以赋值一个逻辑运算
    

    基础对象类型定义

    let object : {
      name : string,
      age : number
    } = {
      name : "测试基础对象定义",
      age : 1
    }
    object = {
      name :"减少、增加属性",
      age : 222,
      adress : "测试"
    }//报错
    

    基础数组类型定义

    let any : string [];
    any = ["a","b","c"]
    any = ["1","2",3]//报错
    let any1 :number[] = [1,2,3];
    

    复杂多类型数组定义

    let any :(number|string)[] = [1,"a","b",2]
    

    利用接口interface定义数组

    //其实一个普通的数组转为对象
    let a = [1,2,3];
    其实他的对象无法类似就是{0:1,1:2,2:3}
    interface List{
      [index:number]:number;//这里的index指向的是数组下标
    }
    let list : List = [1,2,3]
    

    定义函数

    //定义一个没有返回值的函数
    let doSomething : ()=>void=()=>{};
    //返回字符串的函数
    let doSomething1 : ()=>string = ()=>"1"
    //返回数字类型的函数
    let doSomething2 : ()=>number= ()=>1
    //返回一个对象
    let doSomething2 :()=>Person = ()=>new Person();
    

    定义一个自定义类型
    关键字inteface

    interface Person { 
      userName : string,
      age : number
    }
    let xiaoming : Person = {
      userName :"小明",
      age : 20
    }
    

    可选参数、多传参数定义

    //可选参数
    function test(a:string,b?:string):void{};
    function test1(a:string){}
    test1("a","b","c");//会报错
    function test1(a:string,...args:string[]){}
    

    函数的返回类型注解

    //返回值类型注明是string
    function doSomething(name:string):string{
      return "你好啊" + name;
    }
    //没有返回值
    //或者直接可以不写void
    function doSomething(name:string):void{
      return "你好啊" + name;
    }
    let doSomething3 : Function = ():string=>"1" 
    

    函数返回类型有一个特殊的类型never
    那么什么是never
    如果某个函数是never的返回类型,那么代表这个函数被阻断了,不能完全执行完成。

    let neverFn = ():never=>{
      console.log(111);
      while(true){}//死循环
      console.log(222);//不能执行到这里
    }
    
    let neverFn = ():never=>{
      console.log(111);
      throw new Error();
      console.log(222);//不能执行到这里
    }
    

    元祖

    元祖的定义其实就和数组差不多

    let any : [string,string,number] = ["a","b",1]
    

    粗看感觉和普通的多类型数组定义差不多,但是现在类型枚举在括号内,而且每一项必须对应,不能少也不能多,也就是第一项你定义了string类型,那么你数据的第一项必须是string类型,而且有多少值你得定义多少类型,看起来很鸡肋

    接口

    interface 关键字定义一个接口

    基础定义接口

    interface Person{
        name:string;
        age:number;
        say:()=>void
    }
    
    let xiaoming:Person = {
        name:"小明",
        age:21,
        say:()=>`HI I am xiaoming`
    }
    console.log(xiaoming.name);
    console.log(xiaoming.age);
    console.log(xiaoming.say());
    

    任意类型any

    interface Person{
        name:string;
        age:number;
        like:any;
        say:()=>void
    }
    
    let xiaoming:Person = {
        name:"小明",
        age:21,
        like:"游泳",
        say:()=>`HI I am xiaoming`
    }
    console.log(xiaoming.name);
    console.log(xiaoming.age);
    console.log(xiaoming.say());
    

    可有可无属性?

    interface Person{
        name:string;
        age:number;
        like:any;
        say:()=>void;
        desc?:any;
    }
    
    let xiaoming:Person = {
        name:"小明",
        age:21,
        like:"游泳",
        say:()=>`HI I am xiaoming`
    }
    console.log(xiaoming.name);
    console.log(xiaoming.age);
    console.log(xiaoming.say());
    

    联合属性 以|分割属性

    interface Person{
        name:string;
        age:number;
        like:any;
        say:()=>void;
        desc?:any;
        adress:string|string[]|(()=>string);
    }
    
    let xiaoming:Person = {
        name:"小明",
        age:21,
        like:"游泳",
        say:()=>`HI I am xiaoming`,
        adress:"艾欧尼亚"
    }
    console.log(xiaoming.name);
    console.log(xiaoming.age);
    console.log(xiaoming.say());
    

    类的接口定义

    interface Person{
        name:string;
        age:number;
        like:any;
        say:()=>void;
        adress:string|string[]|(()=>string);
    }
    
    let xiaoming:Person = {
        name:"小明",
        age:21,
        like:"游泳",
        say:()=>`HI I am xiaoming`,
        adress:"艾欧尼亚"
    }
    
    class XiaoHong implements Person{
        name ="小红";
        age = 20;
        say=()=>"哈哈哈";
        adress="艾欧尼亚";
        like = "玩游戏";
    }
    

    接口与接口之间的继承extends

    interface Person{
        name:string;
        age:number;
        like:any;
        say:()=>void;
        adress:string|string[]|(()=>string);
    }
    
    interface Teach extends Person{
        job:string;
    }
    
    let wangTeach:Teach = {
        name:"袁华",
        age:45,
        like:"秋雅",
        say:()=>console.log("这道题我不会做"),
        adress:"夏诺特烦恼",
        job:"装男人"
    }
    

    类的定义

    java中一样 也有关键字:
    1、private私有的
    2、protected保护的
    3、public公开的

    public的使用默认属性都会添加

    interface Info{
        name:string,
        age:number
    }
    class Person{
        public name:string;
        age:number;//默认就是public
        constructor(info:Info){
            console.log(info);
            this.age = info.age;
        }
    }
    let xm = new Person({name:"a",age:18});
    xm.name="小明";//可以直接修改
    console.log(xm);
    

    如果某个属性或者方法标注的是public;那么该类被实例化后,可以直接修改属性的值,也可以被继承


    private不可被子类/实例修改值不可被继承
    只能在类的内部被使用

    class Person{
        public name:string;
        private money:number;
        age:number;//默认就是public
    }
    
    class Teach extends Person{
        constructor(name:string){
            super();
            this.name = name;
            this.age = 45;
            this.money = 200000;//报错
        }
    }
    
    let wangTeach = new Teach("王老师");
    console.log(wangTeach.money);//报错
    

    虽然private的属性不能被修改或者读取,但我们可以包装get和set方法

    class Person{
        constructor(private _age:number){}
        get getAge(){//可以进行运算后返回
            return this._age - 10;
        }
        set setAge(age:number){
            this._age = age;
        }
    }
    
    let p = new Person(28);
    //console.log(p._age);//报错
    console.log(p.getAge);//注意!!!这里不是方法调用!!!
    p.setAge = 32;
    
    console.log(p.getAge);//注意!!!这里不是方法调用!!!
    

    注意get、set调用时不在是方法而是属性的方式


    protected可以被继承且能被子级修改值,但不能被外部(实例对象)修改值或访问值

    interface Info{
        name:string,
        age:number
    }
    class Person{
        public name:string;
        private money:number;
        protected like:string[];
        age:number;//默认就是public
    }
    
    class Teach extends Person{
        constructor(name:string){
            super();
            this.name = name;
            this.age = 45;
            this.like = ["教书","看书"]
        }
    }
    
    let wangTeach = new Teach("王老师");
    console.log(wangTeach.like);//报错
    

    类的属性简化定义

    class Person{
      constructor(public name:string){}//简化定义他和下面的语句等价
    /*  name:string;
      constructor(name:string){this.name=name;}*/
    }
    

    如果当前类被继承那么该怎么写呢?而且子级也有一个传参构造函数

    class Person{
      constructor(public name:string){}
    }
    class Teacher extends Person{
      constructor(public age:number){
        super("王老师");//重点
      }
    }
    let t = new Teacher(18);
    console.log(t)
    

    注意当父类不管有没有构造函数constructor只要子级需要使用构造函数,那么super必须被调用

    什么是super:可以理解成调用父类的构造函数方法

    类的静态属性

    在属性或者方法前加上static修饰词,然后使用类名.属性/方法名调用,类似json

    class Persons{
        static userName:string = "小明";
        static sayHello(){
            console.log("你好啊"+this.userName);
        }
    }
    console.log();
    Persons.sayHello();
    console.log(Persons.userName);
    

    类的只读属性

    修饰词:readonly

    
     class Person{
         constructor(public readonly name:string){}
     }
    
     let p = new Person("小明");
     console.log(p.name);
     p.name = "测试"//报错
     console.log(p.name);
    

    抽象类的使用

    修饰词abstract定义的类如果被继承那么子类必须实现被abstract修饰的属性/方法

    abstract class Person{
        abstract say();
        abstract name:string;
    }
    
    class Student extends Person{
        name = "小明";
        say() {
            console.log("我是学生");
        }
    }
    
    class Teacher extends Person{
        name = "王老师";
        say(){
            console.log("我是"+this.name);
            
        }
    }
    

    tsconfig.json文件

    我们知道ts文件最终会被编译成浏览器认识的js文件,那么编译过程我们可以根据我们的配置进行编译,如压缩、去掉注释等配置。
    具体我们可以去查看typescript中文网,或者你可以查看相关博客

    tsc -init //生成tsconfig.json文件
    
    "include":[],只编译定义的文件
    "exclude":[],排除定义的文件进行编译
    "files":[]和include意义类似
    "compilerOptions.removeComments":是否删除注释
    "compilerOptions.strict":js严格模式
    "compilerOptions.outDir":编译后输出到哪里一半我们设置为./build
    "compilerOptions.rootDir":编译的跟文件路劲
    "compilerOptions.sourceMap":生成map文件,改文件的用途是方便在编译后如果调试能够快速找到源文件(ts)出错的位置
    "compilerOptions.noUnusedLocals":定义的变量没有被调用则不会被编译,非常有用
    "compilerOptions.noUnusedParameters":定义的方法没有被调用则不会被编译,非常有用
    、、、、
    

    联合类型和类型保护

    什么是联合类型?当一个变量或者形参的类型是多个的时候,使用|作为多类型注解;
    例如 const anys:()=>string|string|number|boolean;
    什么是类型保护?当一个形参或者变量是联合类型时,我们不确定具体是那种类型,那我们就使用类型保护

    interface Student{
        study:boolean;
        says:Function;
    }
    
    interface Teacher{
        name:string;
        lecture : ()=>{};
    }
    
    function run(animal:Student | Teacher){
        //animal.lecture();//会直接报错,因为animal的类型是两种不确定实参到底是哪个所以我们要类型保护
        if(animal.name){
            (animal as Teacher).lecture();//类型保护
        }else{
            (animal as Student).says();
        }
    }
    function run2(animal:Student | Teacher){
        //animal.lecture();//会直接报错,因为animal的类型是两种不确定实参到底是哪个所以我们要类型保护
        if("name" in animal){//使用in
            (animal as Teacher).lecture();
        }else{
            (animal as Student).says();
        }
        
    }
    

    枚举Enum

    枚举值有一个固定的下标将其定义,当然我们也可以改变每个对应的下标

     enum Status {
         MESSAGE,TEST,MORE,OTHER
     }
     console.log(Status.MESSAGE);//0
     console.log(Status.TEST);//1
     console.log(Status.MORE);//2
     console.log(Status.OTHER);//3
    

    我们也可以只给其实枚举值一个下标,那么其他的对应下标会依次递加

     enum Status {
         MESSAGE=5,TEST=9,MORE=10,OTHER=23
     }
     console.log(Status.MESSAGE);
     console.log(Status.TEST);
     console.log(Status.MORE);
     console.log(Status.OTHER);
    

    我们也可以根据下标反推对应的枚举是什么

    enum Status {
        MESSAGE,TEST,MORE,OTHER
    }
    
    console.log(Status[0]);
    console.log(Status[1]);
    console.log(Status[2]);
    console.log(Status[3]);
    console.log(Status[4]);//undefined
    

    泛型

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

    
    function testq<T>(argus:T):T{
        return argus;
    }
    //多个泛型
    function testqa<T,P>(a:T,b:P){
        return `${a}${b}`;
    }
     function test<T>(argus:T[]){
        return argus.join(",")
     }
    
     console.log(test<String>(["wq","xaa","csq"]));
    

    类中使用泛型

    class Test<T>{
        constructor(private names:T[]){}
        getName(index:number):T{
            return this.names[index];
        }
    }
    

    泛型的继承

    interface Grils{
        name:string;
    }
    
    class SomeSrils<T extends Grils>{
        constructor(private names:T[]){}
        getName(index:number):string{
            return this.names[index].name;
        }
    }
    
    let s = new SomeSrils([{name:"小花"},{name:"小名"},{name:"小兰"}]);
    console.log(s.getName(2));
    

    泛型的类型约束

    可以理解为泛型只能为所提供的的类型中一个

    //该泛型必须是number/string
    class SomeSrilsy<T extends number | string>{
        constructor(private names:T[]){}
        getName(index:number):T{
            return this.names[index];
        }
    }
    
    let sy = new SomeSrilsy([1,"1","s"]);
    console.log(sy.getName(2));
    

    命名空间

    命名空间可以有效防止全局变量被污染
    namespace和``

    namespace Module{
        class Person{
            constructor(public name:string){}
        }
        class Teach{
            constructor(public name:string){}
        }
        export class Student{
            constructor(public name:string){}
        }
        export class Main{
            constructor(){
                new Person("小明");
                new Teach("小话");
                new Student("小兰");
            }
        }
    }
    new Module.Main();
    

    使用tsconfig文件防止多个模块都被编译成多个文件。

    什么意思呢?比如一个项目中有三个模块,但是我们跟文件index.html,只引入了打包后的main.js。但是我们的main.js又引入了其他三个模块组成的

    //tsconfig.js
    "compilerOptions.outFile":"./build/main.js"打包后的唯一文件名
    "compilerOptions.module":"amd"
    

    ts与jq一起使用

    我们知道jq$会在ts中报错

    解决方法一

    直接声明$

    declare var $:any;
    

    解决方法二

    下载@types/jquery

    npm i @types/jquery -D
    

    解决方法三

    创建.d.ts文件对jq进行声明

    相关文章

      网友评论

          本文标题:TypeScript超详细教程

          本文链接:https://www.haomeiwen.com/subject/nmuegktx.html