• 【typescript】写给JS老鸟的TS速成教程


    写给JS老鸟的TS速成教程

    搭建基础开发环境

    要准备的环境

    node.js 14.14以上 ,vs code最新,vs code TS开发插件

    开始开发

    方式一:TS原生编译开发

    补充知识:i是install的简写,-g是global的简写,除此外还有-D = --save-dev 、-S=  --save,现在新版npm cli好像会默认执行—save

    npm i -g typescript && tsc -init

    vs code创建 文件夹/index.ts,

    手动编译 tsc+文件名,改一次编译一次

    自动编译 Ctrl+shift+B —> 监视模式,文件变动时自动编译 或 如下:

    ①    tsc 文件名 -w,缺点是只能监视一个文件,除非开多个命令行。

    ②    创建tsconfig.json,只写一对{},使其符合json规范,然后直接执行 tsc,即可监视所有ts文件的改动。

     

    运行结果:创建index.html, script.import 编译好的index.js,用浏览器打开index.html

    开箱即用见 附件【TS_ori】

     

    编译控制

    tsconfig.json是ts编译器的配置文件,可以对编译进行自定义

    不知道可选值可以先给一个错误的值,编译器会列出所有可选的正确配置值

    tsconfig.json可以写注释,因为其是ts编译器的配置文件。

    {

      // 初步

     

      "include":[ //指定哪些目录的ts文件需要被编译,包含目录

        "./src/**/*" //.当前目录 src文件夹 **任意目录 *任意文件

      ],

      "exclude":[],//指定哪些ts文件不被编译,排除目录,有默认值,比如node_modules

      "extends":"./config/base",//继承某个配置文件,配置复用

      "files":[],//指定ts文件编译,包含文件,与include类似

     

      //进阶

      "compilerOptions":{

        "target":"ES6", //指定ts编译成何种js版本,即目标代码的版本

      //ES2015(ES6),ES2016...ESNext(最新版ES)

      "module":"ES6",//指定模块化解决方案

       "lib":["dom"],//用来指定项目中使用到的库,一般不写此属性(有默认值),除非代码在nodejs中运行,缺少某些库,如dom。dom库为document

      "outDir":"./dist",//指定编译后文件所在目录。dist即distribution,发行版

      "outFile":"./dis/app.js",//将代码合并为一个文件,若需要把各模块合并为一个文件,只能支持amd and system模块方案

      "allowJs":false, //是否对js进行编译,默认否  - false                             //合并文件一般不用outFile,而是结合打包工具来实现

      "checkJs":false, //是否检查js代码是否符合语法规范

      "removeCommentss":true,//是否移除注释

      "noEmit":false,//执行编译但是不生成编译后的文件,场景是只用到ts编码和类型检查, 不须编译

      "noEmitOnError":false,//发送错误时不生成编后的译文件,默认为false,为的是让js程序员缓慢过渡到ts,建议改为true

     

      "strict":false,//以下三个检查的总开关,建议开发时设为true

      "alwaysStrict":false,//指定编译后的js文件是否使用严格模式。严格模式:语法严格,在browser性能好一些

      "noImplicitAny":false,//Implicit隐式的,开启隐式any的检查,不允许使用

      "noImplicitThis":false,//不允许不明确类型的this

      "strictNullChecks":false,//严格检查空值,若一个变量可能为null,则报错,除非先进行非空判断或box?.addEventListener()

     }

    }

    方式二:自动化开发

    补充知识:webpack-cli是通过命令行来使用webpack、ts-loader是webpack加载器,是ts和webpack的桥梁

    补充知识:webpack这东西就是会沿着你给定的一个入口文件去遍历所有关联的文件,然后按照一定规则重新整理、压缩成新的一批文件,我们的文件格式是无穷无尽的,webpack不可能认识和处理全部格式,所以我们通过加入各种loader,称加载器,来帮助webpack认识它们。

     

    创建空文件夹,执行npm init -y(完成项目初始化)

    npm i -D webpack webpack-cli typescript ts-loader

    撰写webpack配置。要注意的是,webpack打包必须配合tsconfig.json使用,也就是你的ts代码处要有这个tsconfig.json文件,因为ts程序的具体编译的工作还是由ts本身提供,而ts编译本身要用到tsconfig.json,现在看来,ts-loader真的仅仅是个桥梁。

    在package.json中加入打包命令build:webpack,执行npm run build。

    撰写配置

    使用chrome来查看ts程序的运行结果

    补充知识:webpack可以支持自动生成html文件并引入打包好的资源,以演示代码效果,这比自己手动写个html方便多了,生成这个html文件有两种方式,配置式,如传个title参数,其他由webpack自己决定,或者自己拟定一个html模板交给插件。

    cnpm i -D html-webpack-plugin

    自动化构建--所写即所见

    cnpm i -D webpack-dev-server ,package.json 加入 "start":"webpack serve --open" ,npm run start

    清楚旧的打包文件

    打包默认模式是用新文件覆盖旧文件,可能存在残留问题,解决方法:clean-webpack-plugin

    指定可引入的文件

    指定哪些文件可以被其他文件作为模块来引入,这里的引入是代码文件之间的引入,这样我们就可以愉快地使用ESM(ES Module)了

    解决目标代码的兼容性问题

    补充知识:前端常见兼容性问题有两种,一种是浏览器内核类,一种是规范版本类。前者主要表现为在Chrome能运行的代码,在Firefox却出现问题,在iPhone默认浏览器能运行的代码在华为默认浏览器却有问题;后者主要表现为ES6的代码在浏览器中报错,因为ES6对比ES5变化是较大的,现在ES规范每年一个版本,浏览器跟进也比以前快了,这个问题正变得越来越不是问题。

    TS的tsc仅仅能够实现把ts源码编译成不同ES规范版本的js代码而已。

    babel/core是babel的核心库、present-env是预置环境,预置不同浏览器环境,帮助代码兼容不同浏览器,babel-loader是结合webpack和babel的桥梁、core-js(Modular standard library )可以使老版本的浏览器使用到新版本的js的一些技术,如promises等,由于这个库比较庞大,内含很多小库,且是模块化的,我们应按需使用,按需使用我们直接通过webpack来实现

    webpack rules的use执行顺序是从下往上执行,我们先用ts-loader把ts转换为js,然后用babel-loader把新版本的js转换为老版本/兼容性高的js

    1. cnpm i -D @babel/core @babel/preset-env babel-loader core-js

    关于兼容性打包后仍报错

    补充知识:设置了targets.ie==11后,打包的代码拿到ie11运行依然报错,原因在于webpack打包后的代码用了一个箭头函数实现的自执行函数包裹,作用是创造一个代码作用域,防止全局变量污染等,它实际是webpack自动生成的,与babel无关,babel只能源文件内的箭头函数起作用,实际上,这可能是webpack故意为之,其本身就是不想兼容某些低版本浏览器,解决方法,out加上environment:{arrowFunction:false},取消箭头函数。

    TS语言

    报错信息,assign:赋值、指派、指定,resolve:解析、决定、解决

    默认ts代码有错误,仍然可以编译生成js,在tsconfig中可更改

    ts可编译成任意版本js

    若赋初值,ts会根据值类型推算变量类型,这会使变量声明加上类型变得多此一举,没错,实际上,类型检测更多是用给函数传参(形参)和返回值的。

    类型

    补充知识:可用字面量代替类型名,如10,以后只能赋值10,有点常量的意思。除此之外,还有联合类型、任意类型等

    talk is cheap,show me the code

     

    export const zex: number = 1;

    {

        let a: number = 10;

        let b: number = 20;

        console.log(a, b)

        //计算变量类型

        let c = true;

        //计算函数返回值类型

        function sum(a: number, b: number): string {

            return a + b + "";

        }

        let result = sum(a, b);

    }

    const zex:number=1;

    {

    // 字面量赋值

    let a:10;

    let a1:number;

    let b:10=10;

    a=10;

    a1=10;

    // 用 或符号 构成 联合类型

    let c:"male"|"female";

    c="male";

    c="female";

    let d:string|number;

    d=10;

    d="hello";

    // 任意类型 - 关闭类型检测

    let e:any;

    e=10;

    e="female";

    e=true;

    // 隐式any

    let f;

    f=10;

    f="female";

    f=true;

    // 赋值

    // 以下不报错,这导致a的类型检测失效

    a1=e;

    // unknown同any差不多,但是可解决以上问题,是一个类型安全的any

    let g:unknown;

    // a=g;报错,应改为

    if(typeof g == "number"){

        a1=g;

    }

    //断言:判断的语言,根据实际情况,把某个变量人为(自己)地断定为某种类型,跳过编译

    a1 = g as number;

    a1 = <number>g;

    //函数的返回值

    //返回值为number|string型

    function sum(a:number,b:number){

        if(a>b){

            return a+b;

        }else{

            return a+b+"";

        }

    }

    function sum2(a:number,b:number):number|string{

        if(a>b){

            return a+b;

        }else{

            return a+b+"";

        }

    }

    // 空返回

    function sum3(a:number,b:number):void{

        return;

    }

    function sum4(a:number,b:number):void{}

    function sum5(a:number,b:number):void{

        return undefined;

    }

    // never:永远不会返回结果

    // 没有返回值也是一种返回值,而never是空空

    // 在程序报错时,代码立即停止执行,程序结束,函数结束,所以永远不会有值返回,事情不会发生

    function err():never{

        throw new Error("err");

    }

    }

    {

    // object其实是无用的,因为ts一且皆对象,并没有起到类型限制的作用

    let a:object;

    a={};

    a=function(){};

    //以下有效

    let b:{name:string};

    b={name:"John"};

    // b={name:"John",age:12}结构不一致报错

    // ?-可选属性

    let c:{name:string,age?:number};

    c={name:"John",age:12};

    c={name:"John"}

    // 任意属性:自由添加属性,新属性未知

    // 新属性名为字符串,属性值为任意类型,propName命名随意

    let d:{name:string,[propName:string]:any}

    d={name:"John",str:123}

    let d1:{name:string,[propName:string|number]:any}

    d1={name:"John",str:123}

    d1={name:"John",123:123}

    // 限制函数,单Function无意义

    let e:(a:number,b:number)=>number

    // 数组

    let f:string[];

    f=["John"]

    let f1:Array<number>;

    f1=[123];

    // 元组:固定长度的数组

    let g:[String,String];

    // 必须符合给定,不多不少

    g=["Hello","Hello"];

    //考虑数据存储与表示分离,数据库存储应简短、非字符串,此时Object并不满足要求

    // 枚举,默认从0开始

    enum Gender{

        male=0,

        female=1

    };

    let h:{name:string,gender:Gender};

    h={name:"Jhon",gender:Gender.female};

    console.log(h.gender,h.gender==Gender.female)

    // & - 与,类型组合

    let i:number&string //无意义

    let i1:{name:string}&{age:number};

    i1={name:"John",age:18};

    // 类型的复用-别名

    let j1:number;

    let j2:1|2|3|4|5;

    let j3:1|2|3|4|5|6;

    type myType=number;

    type myType2=1|2|3|4|5;

    let k1:myType;

    let k2:myType2;

    let k3:myType2|6;

    k3=3

    k3=6

    // k3=7 报错

    }

    // 类

    class Person {

        // 实例属性,通过实例访问

        readonly name: string = "默认名字";

        age: number = 18;

        // 类属性,通过类访问

        static avgAge: number = 18;

        //只读属性

        static readonly baseName: string = "张"

        // 类方法

        sayHello(name: string): string {

            return "Hello";

        }

        static sayHi(name: string): string {

            return "Hi";

        }

    };

    const per = new Person();

    Person.avgAge = 19;

    per.age = 20;

    // Person.name="三三"; // 报错

    // Person.baseName="李"; // 报错

    console.log(Person.avgAge, per.age);



    // 构造器与this

    class Dog {

        name: string;

        age: number;

        constructor(name: string, age: number) {

            // this表示当前实例

            this.name = name;

            this.age = age;

        }

        bark() {

            // 当前调用方法的对象,如dog1.bark(),this为dog1

            console.log(this, "旺旺旺");

        }

    }

    const dog: Dog = new Dog("旺财", 3);

    // 继承

    {

        class Animal {

            name: string;

            age: number;

            constructor(name: string, age: number) {

                this.name = name;

                this.age = age;

            }

            bark() {

            }

        }

        class Dog extends Animal {

            bark() {

                console.log(this.name + this.age + "旺旺旺");

            }

            run() {

                console.log("蹦蹦跳跳");

            }

        }

        class Cat extends Animal {

            sex: string;

            constructor(name: string, age: number, sex: string) {

                // 调用父类构造器

                super(name, age);

                this.sex = sex;

            }

            bark() {

                // 引用父类的方法

                super.bark();

            }

        }

        const cat = new Cat("小喵", 3, "母");

        //抽象类

        //对于某些类,由于本身拿来实例化是不合适的,且我们也不希望被这样做

        //因此我们就把他设为抽象类,只可以继承,不可以实例化

        abstract class Food {

            name: string;

            color: string;

            //  抽象类须有构造器

            constructor(name: string, color: string) {

                this.name = name;

                this.color = color;

            }

            abstract eat(name: string): void;

            abstract cook(name: string): number | string;

        }

        /**

         * 在限制对象的类型上,以下两种方式功能一致

         * 接口,定义了类的结构(属性、方法)

         * 此接口非彼接口,它与Java的接口有点不同

         */

        type myType = {

            name: string,

            age: number

        };

        interface myInterface {

            name: string;

            age: number;

        }

        // 接口可以重复定义,实际效果是同名接口的总和

        interface myInterface {

            sex: string;

        }

        const man: myInterface = {

            name: "张三",

            age: 18,

            sex: "男"

        }

        // 限制类的结构

        interface myInterface2 {

            name: string;

            sayHi(): void;

        }

        interface myInterface3 { }

        class People implements myInterface2, myInterface3 {

            name: string;

            constructor(name: string) {

                this.name = name;

            }

            sayHi(): void {

                console.log(this.name);

            }

        }

        /**

         * 抽象类和接口

         * 抽象类:

         * 1、可以普通方法,也可以抽象方法

         * 2、通过继承来使用,TS 类的设计为单继承

         *

         * 接口:

         * 1、只有抽象方法

         * 2、通过实现来使用,支持多继承

         */

    }

    // 属性的封装 - 保护类的属性

    // 属性可被随意修改将导致对象中的数据变得不安全

    // 通过类修饰符解决这个问题

    // 默认为public,属性可被随意修改

    {

        class Person {

            name: string;

            public sex: string;

            public age: number;

            constructor(name: string, sex: string, age: number) {

                this.name = name;

                this.sex = sex;

                this.age = age;

            }

        }

        // 最强安全性

        // private修饰的属性,只有两种修改方式:

        // 1、构造器传入

        // 2、调用实例方法修改,这种方法在java中被称为setter

        // 此外,这种方式也给属性的访问带来麻烦,我们同样只能通过方法return该属性来访问,在java中称为getter

        // 这种setter和getter模式在C#中被吸收为语法:

        /**

         * get{

                 return _name;

            }

            set{

                _name = value;

            }

         */

        class Person2 {

            private name: string;

            private sex: string;

            private age: number;

            protected height: number;

            constructor(name: string, sex: string, age: number, height: number) {

                this.name = name;

                this.sex = sex;

                this.age = age;

                this.height = height;

            }

            setName(name: string): void {

                this.name = name;

            }

            getName(): string {

                return this.name;

            }

        }

        // 此外,private属性也不可以被子类访问,protected可以

        class Person3 extends Person2 {

            showName(): void {

                // 无法访问name,可以访问height

                // console.log(this.name);

                console.log(this.height);

            }

        }

        // 泛型

        // 当一种类型是什么要在实际中才能确定是,我们使用泛型

        // 泛型就是类型的抽象表示(代表)

        // 当以下函数的参数与返回值类型一致,但是无法确定是什么具体类型时,用泛型

        function fn(a: number): number {

            return a;

        }

        // 能否使用any?不行,一是关掉了类型检查,这将埋下隐患,二是无法体现两者类型一致

        function fn2<T>(a: T): T {

            return a;

        }

        function fn3<T, K, I>(a: T, b: K, c: I): T {

            return a;

        }

        function fn4<T, K>(a: T, b: K, c: number): number {

            return c;

        }

        // 在调用含泛型函数时,一般会自动推定泛型的类型,我们也可手动指定

        fn2(1);

        fn4(1, "2", 3);

        fn4<number, string>(1, "2", 3);

    }

    【附件】

    https://github.com/heytheww/TSLenrning

  • 相关阅读:
    编程算法
    2048小游戏主要算法实现
    Spring 计时器 @Scheduled cron 含义
    jQuery Validate Ajax 验证
    jQuery Validate 使用
    Java 两个整数相除保留两位小数,将小数转化为百分数
    mybatis+mysql返回插入的主键,参数只是提供部分参数
    微信 创建自定义菜单 向微信发起的post请求
    JSON.parse()和JSON.stringify()
    前台JSON字符串,spring mvc controller也接收字符串
  • 原文地址:https://www.cnblogs.com/ww01/p/15942433.html
Copyright © 2020-2023  润新知