zoukankan      html  css  js  c++  java
  • Typescript学习笔记--进阶篇

    1. 类型别名

    类型别名用来给一个类型起个新名字。

    使用 type 创建类型别名。

    类型别名常用于联合类型。

    type Name = string;
    type NameResolver = () => string;
    type NameOrResolver = Name | NameResolver;
    function getName(n : NameOrResolver): Name {
        if (typeof n === 'string') {
            return n;
        } else {
            return n();
        }
    }
    

    2. 字符串字面量类型

    字符串字面量类型用来约束取值只能是某几个字符串中的一个。

    类型别名 与 字符串字面量类型 都是使用 type 进行定义的。

    type EventNames = 'click' | 'scroll' | 'mousemove';
    function handleEvent(ele: Element, event: EventNames){
        // ...
    }
    // 使用 type 定义了一个字符串字面量类型 EventNames,它只能取三种字符串中的一种。
    

    3. 元组

    数组合并了相同类型的对象,而元组(Tuple)合并了不同类型的对象。

    注意:在定义时,就应该初始化。当赋值或访问一个已知索引的元素时,会得到正确的类型。

    // 定义元组
    let tp: [string, number] = ['tuple', 22];
    // 赋值
    tp[0] = 'sssss';
    tp[1] = 25;
    // 获取元素
    tp[0]
    tp[1]
    

    当添加越界的元素时,它的类型会被限制为元组中每个类型的联合类型。就是说添加其他类型的元素会报错。

    比如在上面定义的元组中进行以下操作 tp.push(true) 就会报错,因为 tp 的类型只能是 string 或者 number。

    4. 枚举

    枚举(Enum)类型用于取值被限定在一定范围内的场景,比如一周只能有七天,颜色限定为红绿蓝等。

    enum Days { Sun, Mon, Tue, Wed, Thu, Fri, Sat};

    枚举成员会被赋值为从 0 开始递增的数字,同时也会对枚举值到枚举名进行反向映射。

    enum Days { Sun, Mon, Tue, Wed, Thu, Fri, Sat};
    
    // 枚举值到枚举名进行反向映射
    console.log(Days['Wed'] === 3); // true
    console.log(Days[0] === 'Sun'); // true
    

    4.1 手动赋值

    我们可以给枚举项手动赋值,未手动赋值的枚举项会接着上一个枚举项递增。如果未手动赋值的枚举项与手动赋值的重复了,TypeScript是不会察觉到这一点的,后面的项会覆盖前面的项。

    手动赋值的枚举项也可以为小数或负数,此后续未手动赋值的项的递增步长仍为 1,在小数或负数的基础上加 1

    enum Days {Sun = 7, Mon = 1, Tue, Wed, Thu, Fri, Sat}; // 未手动赋值的枚举项会接着上一个枚举项递增

    4.2 常数项和计算所得项

    枚举项有两种类型:常数项(constant member)和计算所得项(computed member)。

    // 常数项,默认第一项为0;
    // 也可手动赋值第一项,后面的自递增。
    enum Color {
        Red = 1,
        Green,
        Blue
    };
    
    // 计算所得项
    enum Color {
        Red,
        Green,
        Blue = 'blue'.length
    };
    // 如果紧接在计算所得项后面的是未手动赋值的项,那么它就会因为无法获得初始值而报错。
    // 因此计算所得项可以放在后面,否则其后面的项要手动赋值。
    // 也就是说上面定义的 Color 枚举中的 Blue 后面还有新的项,其需要重新手动赋值,否则报错
    

    4.3 常数枚举

    常数枚举是使用 const enum 定义的枚举类型。

    常数枚举与普通枚举的区别:它会在编译阶段被删除,并且不能包含计算成员。若包含计算成员,则会在编译阶段报错。

    const enum Directions {
        Up,
        Down,
        Left,
        Right
    }
    let directions = [Directions.Up, Directions.Down, Directions.Left, Directions.Right];
    
    // 编译结果:
    var directions = [0/*Up*/, 1/*Down*/, 2/*Left*/, 3/*Right*/];
    

    4.4 外部枚举

    外部枚举(Ambient Enums)是使用 declare enum 定义的枚举类型。

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

    外部枚举与声明语句一样,常出现在声明文件中。

    declare enum Directions {
        Up,
        Down,
        Left,
        Right
    }
    let directions = [Directions.Up, Directions.Down, Directions.Left, Directions.Right];
    
    // 编译结果
    var directions = [Directions.Up, Directions.Down, Directions.Left, Directions.Right];
    

    同时使用 declareconst 也是可以的。

    declare const enum Directions {
        Up,
        Down,
        Left,
        Right
    }
    let directions = [Directions.Up, Directions.Down, Directions.Left, Directions.Right];
    
    // 编译结果
    var directions = [0 /* Up */, 1 /* Down */, 2 /* Left */, 3 /* Right */];
    

    5. 类

    传统方法中,JavaScript 通过构造函数实现类的概念,通过原型链实现继承。而在 ES6 中,我们终于引来了 class.

    TypeScript 除了实现了所有 ES6 中的类的功能以外,还添加了一些新的用法。

    5.1 类的概念

    • 类(Class):定义了一件事物的抽象特点,包含它的属性和方法;
    • 对象(Object)类的实例,通过 new 生成;
    • 面向对象(OOP)的三大特性:封装、继承、多态;
    • 封装(Encapsulation):将对数据的操作细节隐藏起来,只暴露对外的接口。外界调用端不需要(也不可能)知道细节,就能通过对外提供的接口来访问该对象,同时也保证了外界无法任意更改对象内部的数据;
    • 继承(Inheritance):子类继承父类,子类除了拥有父类的所有特性外,还有一些更具体的特性;
    • 多态(Polymorphism):由继承而产生了相关的不同的类,对同一个方法可以有不同的响应。比如 Cat 和 Dog 都继承自 Animal,但是分别实现了自己的 eat 方法。此时针对某一个实例,我们无需了解它是 Cat 还是 Dog,就可以直接调用 eat 方法,程序会自动判断出来应该如何执行 eat;
    • 存取器(getter & setter):用以改变属性的读取和赋值行为;
    • 修饰符(Modifiers):修饰符是一些关键字,用于限定成员或类型的性质。比如 public 表示公有属性或方法;
    • 抽象类(Abstract Class):抽象类是供其他类继承的基类,抽象类不允许被实例化。抽象类中的抽象方法必须在子类中被实现;
    • 接口(Interfaces):不同类之间公有的属性或方法,可以抽象成一个接口。接口可以被类实现(implements)。一个类只能继承自另一个类,但是可以实现多个接口。

    5.2 TypeScript 中类的用法

    • public : 修饰的属性或方法是公有的,可以在任何地方被访问到,默认所有的属性和方法都是 public 的
    • private : 修饰的属性或方法是私有的,不能在声明它的类的外部访问
    • protected : 修饰的属性或方法是受保护的,它和 private 类似,区别是它在子类中也是允许被访问的

    注意:TypeScript 编译之后的代码中,并没有限制 private 属性在外部的可访问性。

    抽象类:抽象类是不允许被实例化的,只能用于被继承。

    abstract 用于定义抽象类和其中的抽象方法。而且抽象类中的抽象方法必须被子类实现。

    abstract class Animal {
        public name;
        public constructor(name) {
            this.name = name;
        }
        public abstract sayHi();
    }
    
    class Cat extends Animal {
        public sayHi() {
            console.log(`Meow, My name is ${this.name}`);
        }
    }
    let cat = new Cat('Tom');
    

    5.3 类的类型

    给类加上 TypeScript 的类型很简单,与接口类似:

    class Animal {
        name: string;
        constructor(name: string) {
            this.name = name;
        }
        sayHi(): string {
            return `My name is ${this.name}`;
        }
    }
    let a: Animal = new Animal('Jack');
    console.log(a.sayHi()); // My name is Jack
    

    5.4 ES6 中类的用法

    • class 定义类
    • constructor 定义构造函数
    • new 生成新实例
    • extends 关键字实现继承
    • super 关键字来调用父类的构造函数和方法
    • 使用 gettersetter 可以改变属性的赋值和读取行为
    • 静态方法

    使用 static 修饰符修饰的方法称为静态方法,它们不需要实例化,而是直接通过类来调用。

    class Animal {
        static isAnimal(a) {
            return a instanceof Animal;
        }
    }
    let a = new Animal('Jack');
    // 调用方式,类名.静态方法
    Animal.isAnimal(a);  // true
    

    5.5 ES7 中类的用法

    • 实例属性:直接在类里面定义
    • 静态属性:属性前加上 static 关键字
    class Animal {
        name = 'Jack';      // 实例属性
        static num = 42;    // 静态属性
        constructor() {
            // ...
        }
    }
    

    6. 类与接口

    接口的用途:

    • 用于对 [对象的形状] 进行描述
    • 对类的一部分行为进行抽象

    6.1 类实现接口

    实现(implement)是面向对象中的一个重要概念。一般来讲,一个类只能继承自另一个类,有时候类之间可以有一些共有的特性,这时候就可以把特性提取成接口(interfaces),用 implements 关键字来实现。这个特性大大提高了面向对象的灵活性。

    注意:一个类只能继承自另一个类;但一个类可以实现多个接口。

    6.2 接口继承接口

    接口与接口之间可以是继承关系。

    interface Alarm {
        alert();
    }
    interface LightableAlarm extends Alarm {
        lightOn();
        lightOff();
    }
    

    6.3 接口继承类

    接口可以继承类:

    class Point {
        x: number;
        y: number;
    }
    interface Point3d extends Point {
        z: number;
    }
    let point3d: Point3d = {x: 1, y: 2, z: 3};
    

    6.4 混合类型

    可以使用接口的方式来定义一个函数需要符合的形状:

    interface SearchFunc {
        (source: string, subString: string): boolean;
    }
    
    let mySearch: SearchFunc;
    mySearch = function(source: string, subString: string) {
        return source.search(subString) !== -1;
    }
    

    有时候,一个函数还可以有自己的属性和方法:

    interface Counter {
        (start: number): string;
        interval: number;
        reset(): void;
    }
    
    function getCounter(): Counter {
        let counter = <Counter>function (start: number) { };
        counter.interval = 123;
        counter.reset = function () { };
        return counter;
    }
    
    let c = getCounter();
    c(10);
    c.reset();
    c.interval = 5.0;
    

    7. 泛型

    泛型(Generics)是指在定义函数、接口或类的时候,不预先指定具体的类型,而在使用的时候再指定类型的一种特性。

    7.1 简单例子

    function createArray<T>(length: number, value: T): Array<T> {
        let result: T[] = [];
        for (let i = 0; i < length; i++) {
            result[i] = value;
        }
        return result;
    }
    
    createArray<string>(3, 'x');    // ['x', 'x', 'x']
    
    createArray(3, 'p');    //在调用的时候,可以指定它具体的类型为 string。当然,也可以不手动指定,而让类型推论自动推算出来
    
    // 在函数名后添加了 <T>,其中 T 用来指代任意输入的类型,在后面的输入 value: T 和输出 Array<T> 中即可使用了。
    

    7.2 多个类型参数

    定义泛型的时候,可以一次定义多个类型参数:

    function swap<T, U>(tuple: [T, U]): [U, T] {
        return [tuple[1], tuple[0]];
    }
    
    swap([7, 'seven']); // ['seven', 7]
    

    7.3 泛型约束

    在函数内部使用泛型变量的时候,由于事先不知道它是哪种类型,所以不能随意的操作它的属性或方法。这时,我们可以对泛型进行只允许这个函数传入那些符合要求的变量。

    interface Lengthwise {
        length: number;
    }
    
    function logginIdentity<T extends Lengthwise>(arg: T): T {
        console.log(arg.length);
        return arg;
    }
    // 使用了 extends 约束了泛型 T 必须符合接口 Lengthwise 的形状,也就是必须包含 length 属性。
    // 如果传入的arg不包含length,就会在编译阶段报错了。
    

    7.4 泛型接口

    interface CreateArrayFunc<T> {
        (length: number, value: T): Array<T>;
    }
    
    let createArray: CreateArrayFunc<any>;
    createArray = function<T>(length: number, value: T): Array<T> {
        let result: T[] = [];
        for (let i = 0; i < length; i++) {
            result[i] = value;
        }
        return result;
    }
    
    createArray(3, 'x'); // ['x', 'x', 'x']
    

    7.5 泛型类

    class GenericNumber<T> {
        zeroValue: T;
        add: (x: T, y: T) => T;
    }
    
    let myGenericNumber = new GenericNumber<number>();
    myGenericNumber.zeroValue = 0;
    myGenericNumber.add = function(x, y) { return x + y; };
    

    7.6 泛型参数的默认类型

    当使用泛型时没有在代码中直接指定类型参数,从实际值参数中也无法推测出时,这个默认类型就会起作用。

    function createArray<T = string>(length: number, value: T): Array<T> {
        let result: T[] = [];
        for (let i = 0; i < length; i++) {
            result[i] = value;
        }
        return result;
    }
    

    8. 声明合并

    如果定义了两个相同名字的函数、接口或类,那么它们会合并成一个类型。

    8.1 函数的合并

    // 可以使用重载定义多个函数类型
    function reverse(x: number): number;
    function reverse(x: string): string;
    function reverse(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('');
        }
    }
    

    8.2 接口的合并

    接口中的属性在合并时会简单的合并到一个接口中,合并的属性的类型必须是唯一的。接口中方法的合并,与函数的合并一样。

    interface Alarm {
        price: number;
        alert(s: string): string;
    }
    interface Alarm {
        weight: number;
        alert(s: string, n: number): string;
    }
    
    // 合并成
    
    interface Alarm {
        price: number;
        weight: number;
        alert(s: string): string;
        alert(s: string, n: number): string;
    }
    

    8.3 类的合并

    类的合并与接口的合并规则一致。

    • 属性的合并,合并的是属性的类型必须是唯一的,也就是相同的意思
    • 方法的合并
  • 相关阅读:
    《单元测试之道C#版——使用NUnit》测试哪些内容 RightBICEP
    《领域驱动设计》读书笔记(三) 消化知识
    法雷数列浅谈 RL
    四种常用最短路径算法模板 RL
    PIG 学习笔记1
    readme
    隐藏系统队伍框架
    关于时间的一些函数
    两个作用相同的宏.
    自动拾取确认,自动复活确认
  • 原文地址:https://www.cnblogs.com/AuKing/p/14440651.html
Copyright © 2011-2022 走看看