• TypeScript初识


    # 什么是typeScript
    - typeScript是javaScript的超集,主要提供了类型系统和对ES6的支持。
    - typeScript的代码通过 ts编译工具 可以编译成javaScript代码,从而能在浏览器上运行。类似于java代码需要变成class文件。
    - ts代码 => js代码 需要编译, js代码 => ts代码 直接改后缀名为.ts即可,这也是因为ts是js的超集,它包含js但不限于js。

    # ts的编译工具
    - 上面有提到ts要在浏览器上运行必须编译成js,这里就需要使用ts的编译工具,这个工具其实就是在全局安装tpeyScript。命令如下
    -- npm install -g typescript
    -- 安装之后我们可以在命令行使用 tsc 命令(类似与javac) 对ts文件进行编译。
    -- 例如编译 hello.ts 文件,在命令行执行: tsc hello.ts 命令。这时你的目录里面就会生产一个hello.ts编译之 后的js文件 hello.js

    # ts 初体验
    - 我们先编写一个hello.ts文件,代码如下
    ```javaScript (ts)
    function sayHello(person: string) {
    return 'Hello, ' + person;
    }
    let userName = 'world';
    console.log(sayHello(userName));
    ```
    - 再进行编译 得到hello.js文件,代码如下
    ```javaScript
    function sayHello(person) {
    return 'Hello, ' + person;
    }
    var userName = 'world';
    console.log(sayHello(userName));
    ```
    1. 我们可以看到ts代码中sayHello函数的参数为person: string,规定了只能是字符串类型。如果你传入的是其他类型,编译的时候会编译,但是会报错。

    # 基本数据类型
    - 和js中一样ts也包含布尔值,数值,字符串,null,undefined这集中基本数据类型。当然还有es6中提供的Symbol类型。

    ## 布尔值类型
    - 布尔值作为基本类型中的一种,在ts中使用boolean定义布尔值,注意,不是Boolean,在上面ts初体验中我们定义参数使用的也是string,而不是String

    ```javaScript (ts)
    let isShow : boolean = false; //这里是可以编译通过的
    ```
    - 如果我们ts中像js一样使用构造函数的方法定义一个布尔值是会报错的
    ```javaScript
    let createdByNewBoolean: boolean = new Boolean(1);
    // index.ts(1,5): error TS2322: Type 'Boolean' is not assignable to type 'boolean'.
    /**
    * 事实上在ts中boolean表示的是基本数据类型呢,let createdByNewBoolean: boolean 实际上就是规定了createdByNewBoolean只能是boolean类型。
    * 而Boolean无论是在js中还是ts中他都是一个构造函数,通过new构造函数的方式得到值就是一个对象,而对象是属于引用数据类型。
    * 其实在js中也是一样,只是js没有如此严格类型系统。
    * 这一点ts的基本类型中其他基本类型都是一样,后面就不再赘述(除了null和undefined)
    */

    ```
    ## 数值类型
    - ts中使用number定义数值类型,直接上代码吧
    ```javaScript (ts)
    let decLiteral: number = 6;
    // 十六进制
    let hexLiteral: number = 0xf00d;
    // ES6 中的二进制表示法
    let binaryLiteral: number = 0b1010;
    // ES6 中的八进制表示法
    let octalLiteral: number = 0o744;
    let notANumber: number = NaN;
    let infinityNumber: number = Infinity;
    ```
    -以上ts代码经过编译之后
    ```javaScript
    var decLiteral = 6;
    // 十六进制数
    var hexLiteral = 0xf00d;
    // ES6 中的二进制表示法
    var binaryLiteral = 10;
    // ES6 中的八进制表示法
    var octalLiteral = 484;
    var notANumber = NaN;
    var infinityNumber = Infinity;
    /**
    * 我们可以看到ts经过编译以后,2进制,8进制都被转化成了10进制数。16进制没有。
    * 为啥16进制不转呢?
    * Infinity是啥?简单的说(复杂的我也不会)他是表示所有超出javaScript处理范围的数值。他可以进行运算,但是结果不是Infinity(加)就是NaN(乘除减)。
    * 我们知道数学中0不能作为除数,任何数除以0都是没有意义的。在js中也是一样,任何数除以0得到的值都是Infinity包括Infinity本身(0除外,0/0=NaN)。
    */
    ```
    ## 字符串类型
    - 前面有提到ts中使用string来定义字符串

    ```javaScript (ts)
    let myName: string = 'Tom';
    let myAge: number = 25;
    // 模板字符串
    let sentence: string = `Hello, my name is ${myName}.I'll be ${myAge + 1} years old next month.`;
    ```
    - 以上ts代码编译之后得到以下js代码
    ```javaScript
    var myName = 'Tom';
    var myAge = 25;
    // 模板字符串
    var sentence = "Hello, my name is " + myName + ". I'll be " + (myAge + 1) + " years old next month.";
    /**
    * 我们可以到定义的方式语法和数值布尔值类型是一样的,平时我们工作也会使用到es6中模版字符串,使用``包裹模版,${}包裹变量
    * 编译之后就回到了我们js中相对low逼的字符串拼接模式。
    */
    ```
    ## 空值(ts新增基本类型)
    - ts中使用viod来定义空值,但是我们一般不会去定义一个空值,因为你只能给他赋值null或者undefined。
    - 我们可以用viod来表示一个函数没有没有任何返回值

    ```javaScript (ts)
    let unusable: void = undefined; // 你只能给他赋值unll和undefined
    function alertName(): void { //这表示alertName函数没有任何返回值
    alert('My name is Tom');
    }
    ```
    ## null和undefined
    - 这是两种基本数据类型,由于特点相似所以放在一起讲
    - null和undefined 是所有类型的子类型,也就是说,不论你定义的任何数据类型的变量都可以赋值null和undefined。
    - 这也是null/undefined 和 空值viod的不同之处,被定义为viod类型的变量是不能赋值到其他类型的数据的。
     
    ```javaScript (ts)
    /**
    * 我们前面提到定义void的变量可以给他赋值null和undefined的,
    * unll和undefined可以赋值给任何类型的变量,那么我先给void变量u赋值unll, 再将变量u赋值给数值类型n会不会报错呢?
    * 如下列代码,是会报错的。
    */

    let u:void = null
    let n:number = u

    let a:number = null
    ```

    ## 任意值
    - 任意值在ts中使用any定义。
    - 个人理解是和js中定义变量一样,当你规定这个变量是一个any类型的时候,你可以给他赋值任何类型的数据。
    - 对任意值类型进行任何操作得到的数据都是任意值类型。

    ```javascript (ts)
    let myFavoriteNumber: any = 'seven';
    myFavoriteNumber = 7;
    console.log(myFavoriteNumber);
    myFavoriteNumber = [];
    console.log(myFavoriteNumber);
     
    /**
    * 当定义后没有马上赋值,并且没有定义类型。默认为任意类型
    */

    let arry;
    arry = 0;
    console.log(arry);
    arry = [1,2,3]
    console.log(arry);
    ```
    ```javascript
    var myFavoriteNumber = 'seven';
    myFavoriteNumber = 7;
    console.log(myFavoriteNumber);
    myFavoriteNumber = [];
    console.log(myFavoriteNumber);
    /**
    * 当定义后没有马上赋值,并且没有定义类型。默认为任意类型
    */
    var arry;
    arry = 0;
    console.log(arry);
    arry = [1, 2, 3];
    console.log(arry);
    ```
    ### 类型推论,恰好讲到任意类型先定义后赋值,该定义数据默认为any类型,Ts中还有类型推论规则
    - 当你定义数据没有定义类型的时候,并且在定义的同时给他赋值了,那么ts就会推论,你这个数据为你所赋值的类型。
    - 这里需要和上面先定义后赋值的情况区分。
    - let arry; ==> arry 为任意值类型, let arry = '123' ==> arry 为字符串类型,后续也只能使用字符串为他赋值

    ## 联合类型
    - 顾名思义,表示取值可以为多种类型之一
    - 定义类型时使用 | 将类型隔开
    ```javascript (ts)
    let lianhe: string | number = 3;
    lianhe = 'seven';
    lianhe = 7;
    ```
    ```javascript
    var lianhe = 3;
    lianhe = 'seven';
    lianhe = 7;
    ```
    ### 访问联合属性的类型和方法

    ```javascript (ts)
    /**
    * something是一个联合类型,他可以是string,也可以是number
    * 在函数getLength中我们我们尚不清楚他传入的是什么类型,所以只能调取string和number的公共属性或者方法。
    *
    */
    function getLength(something: string | number){
    // return something.length; // 报错
    return something.toString(); // 不报错
    }
    /**
    * 联合类型也适用类型推论
    */
    let abc : string | number;
    abc = 100 ; // 此时犹豫类型推论原则,ts认为abc是一个数字
    // console.log(abc.length) // 报错
    abc = 'hello' ;
    console.log(abc.length) // 5
    ```

    # 引用类型

    ## 对象的类型-接口
    - 在ts中我们使用interfaces来定义对象的类型
    - 什么是对象的类型,我是这么理解,他是一个规则,假设你通过某个接口去定义了一个对象,那么这个对象必须按照这个接口规则去写。
    - 教程中原文是这么说:TypeScript 中的接口是一个非常灵活的概念,除了可用于对类的一部分行为进行抽象以外,也常用于对「对象的形状(Shape)」进行描述。
    - 一个简单的例子
    ```javascript
    // 定义接口
    interface Person { // 接口一般首字母大写。有的编程语言中会建议接口的名称加上 I 前缀。
    name: string;
    age: number;
    }
    // 通过定义的接口,来约束对象(定义对象)
    let tom: Person = {
    name: 'Tom',
    age: 25
    };
    ```
    ### 接口特点
    1. 固定属性-当你使用接口去约束你定义的对象后,你为你对象定义的属性/方法,都应该和接口一致,包括数量和命名
    2. 可选属性-约束得太死难免会有不愉快的事情发生,所以接口允许设置可选属性, 属性名后面加一个问号即可。
    - 可选属性可以不存在。
    3. 任意属性
    - 使用 [propName: string] 定义了任意属性取 string 类型的值。
    - 需要注意的是,一旦定义了任意属性,那么确定属性和可选属性的类型都必须是它的类型的子集
    - 目前我认为这个属性有点鸡肋,放开一个属性,限制了可选属性和确定属性。
    4. 只读属性
    - 通过readonly定义只读属性
    - 只读属性必须在定义类的时候定义并赋值,后期不可修改。

    ## 数组类型
    1. 在ts中数组类型的定义有很多种
    - 「类型 + 方括号」表示法
    - 规定了数组成员的类型,不能在该数组中添加其他类型属性(null和undefined除外,上面有讲,他是其他类型子集)
    ```javascript (ts)
    let arryList:number[] = [1,2,3,4,5] // 定义arrayList为一个数值数组
    console.log(arryList);
    arryList.push(10);
    arryList.push(null);
    arryList.push(undefined);
    // arryList.push('1'); // 往里面添加字符串是会报错的。
    console.log(arryList);
    // 当然如果你定义的类型为any,那你就可以随意push进任意数据类型了
    ```
    2. 用接口定义数组 (接口是用来定义对象的类型和形状的。万物皆对象,数组也是对象咯)

    ```javascript (ts)
    interface NewArry {
    [a : number] : number; //定义这个数组对象的每一项只能是数值
    }
    let arr1:NewArry = [1,2,3,4,"1"];
    ```
    3. 还有一种方式叫做数组泛型,在此不做分享,后面进阶内容有详细解说。

    ## 类数组
    - 类数组不属于数组类型,比如arguments:
    - 事实上常见的类数组都有自己的接口定义,如 IArguments, NodeList, HTMLCollection 等:
    ```javascript (ts)
    function sum() {
    let args: number[] = arguments; // 会报错
    }
    function sum2() {
    let args: IArguments = arguments; // 使用他们自己的接口定义就不会报错
    }
    ```

    ## 函数
    - 在ts中函数也和js中一样,使用函数声明和函数表达式定义函数
    - 不同的是ts对函数输入和输出进行了更多的约束
    1. 定义规则
    - 输入多余的(或者少于要求的)参数,是不被允许的。
    2. 函数声明
    ```javascript (ts)
    function fn1( x : number,y : number ) : number {
    return x+y
    }
    ```
    3. 函数表达式
    ```javascript (ts)
    /**
    * 这里的 => 要和es6中的箭头函数区分,=> 在这里 左边表示函数的参数定义,右边表示函数的返回值定义。
    */
    let fn2 : ( x: number, y : number ) => number = function( x: number, y: number) : number {
    return x+y
    }
    ```
    4. 接口定义函数
    ```javascript (Ts)
    // 定义接口
    interface SearchFunc {
    (source: string, subString: string): boolean;
    }
    // 接口约束函数
    let fn3: SearchFunc;
    fn3 = function(source: string, subString: string) {
    return source.search(subString) !== -1;
    }
    ```

    5. 可选参数
    - 可选参数使用 ?标识
    - 需要注意的是,可选参数必须接在必需参数后面。换句话说,可选参数后面不允许再出现必须参数了
    ```javascript (ts)
    function fn4(firstName: string, lastName?: string) {
    if (lastName) {
    return firstName + ' ' + lastName;
    } else {
    return firstName;
    }
    }
    let tomcat = fn4('Tom', 'Cat');
    let tom = fn4('Tom');
    ```

    6. 参数默认值
    - 在 ES6 中,我们允许给函数的参数添加默认值,TypeScript 会将添加了默认值的参数识别为可选参数
    - 此时就不受「可选参数必须接在必需参数后面」的限制了
    ```javascript (Ts)
    function fn5(firstName: string = 'Tom', lastName: string) {
    return firstName + ' ' + lastName;
    }
    let tomcat = fn5('Tom', 'Cat');
    let cat = fn5(undefined, 'Cat');
    ```
    7. 剩余参数
    - ES6 中,可以使用 ...rest 的方式获取函数中的剩余参数(rest 参数)
    - 剩余参数只能是最后一个参数
    ```javascript (Ts)
    function push1(array, ...items) {
    items.forEach(function(item) {
    array.push(item);
    });
    }

    let a = [];
    push(a, 1, 2, 3);
    // 事实上,剩余参数 items 是一个数组。所以我们可以用数组的类型来定义它:
    function push2(array: any[], ...items: any[]) {
    items.forEach(function(item) {
    array.push(item);
    });
    }

    let a = [];
    push2(a, 1, 2, 3);
    ```

    8. 重载
    - 什么是重载,就是函数的方法名相同,但是参数不同,调用的时候根据参数的不同去执行不同的执行体。
    - js中不存在重载,如果方法名相同,他永远执行的是写在最后的一个方法。
    - 重载允许一个函数接受不同数量或类型的参数时,作出不同的处理。
    - 比如,我们需要实现一个函数 reverse,输入数字 123 的时候,输出反转的数字 321,输入字符串 'hello' 的时候,输出反转的字符串 'olleh'。
    - 利用联合类型,我们可以这么实现
    ```javascript (Ts)
    function reverse1( x: string | number) : number|string{
    if( typeof x === 'number') {
    return Number(x.toString().slipt('').reverse().join(''))
    }else if(typeof x === 'string'){
    return x.slipt('').reverse().join('')
    }
    }
    /**
    * 然而这样有一个缺点,就是不能够精确的表达,输入为数字的时候,输出也应该为数字,输入为字符串的时候,输出也应该为字符串。这时,我们可以使用重载定义多个 reverse 的函数类型
    */
    function reverse2(x: number): number;
    function reverse2(x: string): string;
    function reverse2(x: number | string): number | string {
    if (typeof x === 'number') {
    return Number(x.toString().split('').reverse().join(''));
    } else if (typeof x === 'string') {
    return x.split('').reverse().join('');
    }
    }
    /**
    * 上例中,我们重复定义了多次函数 reverse,前几次都是函数定义,最后一次是函数实现。
    * 注意,TypeScript 会优先从最前面的函数定义开始匹配,所以多个函数定义如果有包含关系,需要优先把精确的定义写在前面
    */
    ```
    ## 类型断言
    1. 语法
    - <类型>值
    - 值 as 类型 ==> 在 tsx 语法(React 的 jsx 语法的 ts 版)中必须用后一种。

    2. 意义
    - 之前提到过,当 TypeScript 不确定一个联合类型的变量到底是哪个类型的时候,我们只能访问此联合类型的所有类型里共有的属性或方法
    - 这时我们就可以使用类型断言,将一个联合类型的变量指定为一个更加具体的类型,从而使用这个类型的属性

    3. 类型断言,不是类型转换,断言一个联合类型中不存在的类型是会报错的。

    ```javascript (ts)
    function getLength(something: string | number): number {
    if (something.length) {
    return something.length;
    } else {
    return something.toString().length;
    }
    }
    // 以上代码是会报错的。在没有断言的情况下,只能直接访问联合类型的公共属性
    function getLength(something: string | number): number {
    if ((<string>something).length) {
    return (<string>something).length;
    } else {
    return something.toString().length;
    }
    }
    ```
    ## 内置对象
    1. JavaScript 中有很多内置对象,它们可以直接在 TypeScript 中当做定义好了的类型。
    - 内置对象是指根据标准在全局作用域(Global)上存在的对象。这里的标准是指 ECMAScript 和其他环境(比如 DOM)的标准。

    2. ECMAScript
    - 标准提供的内置对象有 : Boolean、Error、Date、RegExp 等
    - 我们可以在 TypeScript 中将变量定义为这些类型:
    ```javascript (ts)
    let b: Boolean = new Boolean(1);
    let e: Error = new Error('Error occurred');
    let d: Date = new Date();
    let r: RegExp = /[a-z]/;
    ```
    3. DOM 和 BOM 提供的内置对象有:Document、HTMLElement、Event、NodeList 等。
    ```javascript (ts)
    let body: HTMLElement = document.body;
    let allDiv: NodeList = document.querySelectorAll('div');
    document.addEventListener('click', function(e: MouseEvent) {
    // Do something
    });
    ```
    4. 使用ts写node.js
    - Node.js 不是内置对象的一部分,如果想用 TypeScript 写 Node.js,则需要引入第三方声明文件:
    npm install @types/node --save-dev


  • 相关阅读:
    char 型变量中能不能存贮一个中文汉字,为什么?
    du 和 df 的定义,以及区别?
    怎样查看一个 linux 命令的概要与用法?假设你在/bin 目录中偶然看到一个你从没见过的的命令,怎样才能知道它的作用和用法呢?
    把后台任务调到前台执行使用什么命令?把停下的后台任务在后台执行起来用什么命令?
    如何使用 Spring Boot 实现分页和排序?
    如何在 Spring Boot 中禁用 Actuator 端点安全性?
    Dubbo 集群容错有几种方案?
    Collection和 Collections的区别?
    当你需要给命令绑定一个宏或者按键的时候,应该怎么做呢?
    怎样一页一页地查看一个大文件的内容呢?
  • 原文地址:https://www.cnblogs.com/boye-1990/p/11039862.html
Copyright © 2020-2023  润新知