• 3、TypeScript高级数据类型


    1、介绍

    上一篇(TypeScript常见的数据类型):http://t.csdn.cn/UjGrn

    TS中除了上篇文档中描述的常见数据类型之外,还有很多高级数据类型,本文档列举一些常用的类型。

    • class 类定义
    • 类型兼容性
    • 交叉类型
    • 泛型 和 keyof
    • 索引签名类型 和 索引查询类型
    • 映射类型

    2、使用

    一、class 类定义

    TS全面支持ES6规范中的class类定义,并且对其进行了增强,如添加了类型注解、实现接口、可见性修饰符等等。

    /**
     * TS中的class,不仅提供了class的语法功能,定义的类也作为一种类型存在
     */
    class Person{
        name: string;
        age: number;
        constructor(name: string,age: number){
            this.name = name;
            this.age = age;
        }
    }
    const p: Person = new Person("张三",18);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    实现接口:ES规范中的class类可以继承父类,TS对其进行了增强,使其可以实现接口。

    /**
     * class类通过implements关键字实现接口
     * 子类必须显示提供父接口中所有属性
     * 子类可以同时实现多个接口,使用 , 分隔
     */
    interface Person{
        name: string,
        say: Function
    }
    interface Behave{
        eat(): void
    }
    
    class Student implements Person,Behave{
        name = '姓名';
        say = ()=>{
            console.log('say hello');
        }
        eat = ()=>{
            console.log('吃饭');
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    可见性修饰符:TS对class类 属性/方法 添加了可见性修饰符,用来控制class的方法或属性对于class外的代码是否可见。

    可见性修饰符包括:1 public(公共的,默认) 2 protected(受保护的) 3 private(私有的)。

    public修饰符

    /**
     * public:表示该属性为公开的,在任何地方均可访问(默认修饰符,可省略)
     * 当前类内部、子类、当前类外部均可访问
     */
    class Person{
        public name: string = '名字'; //同 name: string; 写法作用一致
        getName = ()=>{
            return this.name; //本类中可访问
        }
    }
    
    let p = new Person();
    console.log(p.name);//Person类外部也可访问
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    protected修饰符

    /**
     * protected:表示受保护的,仅对其声明所在类和子类中(非实例对象)可见。
     */
    class Person{
        protected name: string = '名字'; 
        protected getName = ()=>{
            return this.name; //本类中可访问
        }
    }
    
    class Student extends Person{
        printName = ()=>{
            console.log(this.name); //子类中可访问
        }
    }
    
    let s = new Student();
    s.printName();
    let p = new Person();
    //console.log(p.name); //Person类外部不可访问,编译报错
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    private修饰符

    /**
     * private:表示私有的,只在当前类中可见,对实例对象以及子类也是不可见的。
     */
    class Person{
        private name: string = '名字'; 
        private getName = ()=>{
            return this.name; //本类中可访问
        }
    }
    
    class Student extends Person{
        printName = ()=>{
            //console.log(this.name); //编译报错,子类无法访问
        }
    }
    
    let s = new Student();
    //s.name; //Person类外部不可访问,编译报错
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    readonly修饰符:表示属性只读,用来防止在构造函数之外对属性进行赋值

    class Person{
        readonly name: string = '名字'; 
    
        constructor(name: string){
            this.name = name; //构造函数可以为readonly属性赋初值
        }
    
        setName = (name: string){
            //this.name = name; //编译报错:Cannot assign to 'name' because it is a read-only 
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    二、类型兼容性

    两种类型系统:1:Structural Type System(结构化类型系统),2:Nominal Type System(标明类型系统)。

    TS采用的是结构化类型系统,类型检查关注的是值所具有的结构,如果两个对象具有相同的结构,则认为它们属于同一类型。

    标明类型系统,两个对象的类型若要相等,就必须具有相同“名字”的类型(Java、C#)。

    class类兼容

    class Position{x: number=0;y: number=0}
    class Position2{x: number=0;y: number=0}
    class Position3{x: number=0;y: number=0;z: number=0}
    
    //Position、Position2结构相同,所以类型相同
    const p: Position = new Position2(); 
    /**
     * Position3包含Position2所有的结构(Position2⊆Position3)
     * 所以Position2可以作为Position3的对象的类型,反之则不成立
     */
    const p2: Position2 = new Position3();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    接口兼容

    interface Position{x: number;y: number}
    interface Position2{x: number;y: number}
    interface Position3{x: number;y: number;z: number}
    
    /**
     * 接口兼容性与class类兼容性类似
     */
    let p: Position = {x:0,y:0};
    let p2: Position2 = p;
    let p3: Position3 = {x:0,y:0,z:0};
    p2 = p3;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    函数兼容

    函数兼容性涉及到:1、参数个数,2、参数类型,3、返回值类型。
    待续。。。

    三、交叉类型

    交叉类型(&):功能类似于接口继承(extends),用于组合多个类型为一个类型(常用于对象类型)。

    interface A{a: number};
    interface B{b: number};
    type AB = A&B;
    
    //使用类型交叉后,类型AB就具有了类型A、B的属性结构
    let ab: AB = {
        a:1,
        b:2
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    交叉类型(&)和接口继承(extends)的对比:

    ​ 相同点:都可以实现对象类型的组合。

    ​ 不同点:两种方式实现类型组合时,对于同名属性之间,处理类型冲突的方式不同。

    interface A{a: number};
    interface B{a: string,b: number};
    type AB = B&A;
    /**
     * 当A、B接口中存在同名属性时,可以使用类型交叉
     * 注意:当同名属性不同类型时,可能会被合并为 never 类型(不能被满足的类型)
     */
    // let ab: AB = {
    //     a:1,//此时属性a为never类型(不能满足a即为number、又为string),编译报错
    //     b:2
    // }
    
    // interface C extends A{
    //     a: string //直接编译报错,类型冲突
    // }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    四、泛型

    泛型是可以在保证类型安全(不丢失类型信息)前提下,让函数等与多种类型一起工作,从而实现复用,常用于:函数、接口、class中。

    泛型函数

    /**
     * 创建泛型函数
     *  语法:在函数名称的后面添加 <>(尖括号),尖括号中添加类型变量,比如此处的Type(符合命名规范即可)。
     *  类型变量 Type,是一种特殊类型的变量,它处理类型而不是值。
     *  因为 Type 是类型,因此可以将其作为函数参数和返回值的类型,表示参数和返回值具有相同的类型。
     */
    function fun<Type>(param: Type): Type{
        return param;
    }
    
    let numResult = fun<number>(10);
    let strResult = fun<string>('10');
    /**
     * 调用泛型函数时,可以省略 <类型> 来简化泛型函数的调用
     * 充分利用TS的类型参数推断机制,TS根据实际参数类型反推Type类型
     * 说明:当编译器无法推断类型或者推断的类型不准确时,就需要显式地传入类型参数
     */
    let booleanResult = fun(true);
    
    /**
     * 为泛型添加约束
     *  通过extends关键字,确保传入的参数类型继承过IAnimal接口,或拥有name属性(参考类型兼容性)
     *  为什么使用泛型约束:类型变量可以代表任意类型,同时也导致实际传入的对象无法访问任何属性,有时就需要为泛型添加约束来收缩类型(缩窄类型取值范围)。
     */
    interface IAnimal{name: string}
    function eat<T extends IAnimal>(animal: T):void{
        console.log(animal.name);//如果不添加IAnimal类型约束,此处访问name属性就会报错:Property 'name' does not exist on type 'T'
    }
    
    /**
     * 泛型变量可以多个
     */
    function estimate<T,R>(par1: T,par2: R):void{}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    泛型接口

    /**
     * 在接口名称的后面添加 <>(尖括号),那么这个接口就变成了泛型接口。
     * 接口的类型变量,对接口中所有其他成员可见,也就是接口中所有成员都可以使用类型变量。
     */
    interface GenInter<S,N> {
        name: S,
        age: N,
        introduce: (name:S,age:N)=>void
    }
    
    /**
     * 使用泛型接口时,需要显式指定具体的类型。
     */
    const person: GenInter<string,number> = {
        name:'张三',
        age:18,
        introduce: (name,age)=>{
            console.log(`my name is ${name} and I am ${age} years old`);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    泛型class

    /**
     * 在类名称的后面添加 <>(尖括号),那么这个类就变成了泛型类。
     */
    class Person<T,K>{
        name: T;
        age: K;
        constructor(name: T,age: K){
            this.name = name;
            this.age = age;
        }
    }
    /**
     * 在创建 class 实例时,在类名后面通过 <类型> 来指定明确的类型。
     */
    const p = new Person<string,number>("张三",18);
    console.log(p.name,p.age);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    泛型工具类:TS内置了很多基于泛型实现的常用工具类,来简化TS中的一些常见操作。如PartialReadonlyPickRecord等。

  • 相关阅读:
    【网络篇】第九篇——多线程版的TCP网络程序
    c++ 新特性 std::bind 简单实验
    每日一题 2530. 执行 K 次操作后的最大分数(中等,最大根堆)
    Elasticsearch:Flattened 数据类型映射
    MapReduce综合应用案例 — 招聘数据清洗
    记首次协助搭建服务器
    【明年找到好工作】:面试题打卡第三天
    多维时序 | MATLAB实现PSO-GRU-Attention粒子群优化门控循环单元融合注意力机制的多变量时间序列预测
    Android 关于IC卡的读写和加密
    ​调用Lua脚本tostring(xxx)报attempt to call a nil value (global ‘tostring‘
  • 原文地址:https://blog.csdn.net/xiao_yu_gan/article/details/126795824