zoukankan      html  css  js  c++  java
  • Typescript高级类型与泛型难点详解

    最近做的TS分享,到了高级类型这一块。通过琢磨和实验还是挖掘出了一些深层的东西,在此处做一下记录,也分享给各位热爱前端的小伙伴。
     
    其实在学习TS之前就要明确以下几点:
     
    1. typescript 是javascript的超集,这点是官方文档最先说明的,但是具体怎么理解就千差万别了。其实通俗的来说,ts语法就是基于js的一种通用规范,也就是js语法糖。
     
    2. typescript是基于js的一门强类型的高级语言,而ts的所有新增语法都只是在编译环境有效,都只是在编译环境做的类型或语法的检验,而这些新增语法也都是为了使ts这门语言更方便地、更高效地进行大型项目的开发。而编译出来的js文件中也不会有半点ts的新增语法。因为是国际大厂造的轮子,所以代码的可执行性和兼容性都依赖于ts的编译器,这点我们不必过多担心。
     
    3. 就像是高级语言编译为汇编语言或再编译为机器语言一样,其实js也是要通过js解释器去工作的,因为js本身就是一种解释型语言,而js解释器或市面上的多种浏览器内核以及chrome的v8引擎,都是再往更底层的代码去转换。就像ts代码需要使用ts编译器转化为js代码一样。只不过如果只接触过js和ts两种语言的话,可能就会觉得js是偏底层的代码了,但其实这么理解是不对的。
     
    下面回归到本次的正题:
     
    1. ts中字面量的特殊性
    大家熟悉了TS的类型兼容性之后应该会有所了解,强类型语言中变量的赋值或引用是很严格的。
    而在TS中,下面的写法都不会报错:
    let test: {};
    test = 1;
    test = null;
    test = false;
    只举几个例子就可以了,也就是说所有类型都可以赋值为字面量类型。这就需要说到js中一切都是对象这个关键点了,js中,在生成变量时都会临时new一个对应类型的包装对象。所以与上文中字面量类型的test变量兼容也就不足为奇了。
     
    2. 交叉类型(&)与联合类型(|)字面的意思所带来的坑
    可能会有人在听到交叉类型时,会认为是两种或多种类型的交集所产生的类型,首先要说明这么理解是错误的。所以,其实交叉类型更多的带有一些并集的意思,即新生成的类型,会拥有所有子类型的特性。而说起联合类型,其实“联合”二字,在不同的应用环境下,效果也是不尽相同的:
    function printer (param: string | number) {
        console.log(param)
    }
    做类型检验是联合类型最常用的一种方式,此处意为string类型或number类型都可以通过校验。
    确定无误后,带着这个结论继续往下看:
    class Dog {
        eat () {}
        guardHome () {}
    }
    class Cat {
        eat () {}
        catchMice () {}
    }
    function animalFactory (): Dog | Cat {
        if (Math.random() * 10 > 5) {
            return new Dog();
        } else {
            return new Cat();
        }
    }
    let ani = animalFactory();
    ani.guardHome(); // error
    ani.eat()
    

    根据上面的结论, animalFactory 的返回值应该是Dog类或Cat类的实例都可以,但是却偏偏只有eat方法能调用成功,属于各自单独类的guardHome或 catchMice方法都不能调用成功。

    所以联合类型在这种使用环境下,就是两种类型的实例都能调用的成员变量或函数,在此处才可以按照字面的意思去理解。
     
    3. 类型保护的特殊点
    类型保护,顾名思义,即当前类型为保护类型,(假设)确定的类型。
    那么,在某个作用域内,到底从何时开始,对应变量开始成为保护类型了呢?
     
    首先类型会出出现类型保护的三种情况:
     
    (1) 类型断言
    let str;
    let val: number = (str as string).length;
    let val2: number = (<string>str).length;
    (2) 使用类型谓词进行自定义类型保护
    let str;
    function checkString (str: any): str is string {
        return str;
    }
    (3) 使用typeof 和 instanceof
    此方法就不过多说明
     
    下面这种情况会帮助你更好的理解类型保护:
    function checkString (param: number | string) {
        if (typeof param === 'string') {
            let temp = param;  //ok 此处param为string
            param += '1';   //ok  此处param为 string | number
            param += '1';   //ok 此处param为 string | number
            param += 1;    // ok 此处string类型可以与数字相加
            return param;   // 此处param为number
        } else {    // 此处此处相当于 if (typeof param === 'number')
            param += 1;  // ok 此处param为 string | number
            param += '1';   // error  number类型不能与字符串相加
            return param   // 此处param为number
        }
    }
    首先参数中的 string|number 是我们手动制定的类型要求,而在两个大括号的类型保护区间内,我们发现只有当发生赋值或产生结果(如返回值)时,才会发生真正的类型保护,即类型推断变为类型保护。
     
    4.  keyof(索引类型操作符)与泛型混合的多种写法
    首先 ,keyof 意为 某一数据类型的key的数组集合,既适用于数组,也适用于对象。下文会做验证:
    interface testInter {
        name: string,
        age: number
    }
    let testArr: string[] = ['tate', 'pomelott'];
    let testObj: testInter = {name: 'tate', age: 26}

    先来验证数组:

    function showKey<K extends keyof T, T> (key: K, obj: Array<string>) {
        return key;
    }
    showKey<number, Array<string>>(1, testArr);

    再来验证对象:

    function showKey<K extends keyof T, T> (keyItem: K, obj: T): K {
        return keyItem;
    }
    let val = showKey('name', testObj)

    此处有个需要特别注意的点:使用泛型如何表示某个特定key组成的数组

    function showKey<K extends keyof T, T> (items: K[], obj: T): T[K][] {
        return items.map(item => obj[item])
    }

    上例中的  T[K][] 意为K类型的数组,而且需要满足,K为T的key

    真正理解了上面这句话,自然就会明白下面四种写法其实是等价的:

    function showKey<K extends keyof T, T> (items: K[], obj: T): T[K][] {
        return items.map(item => obj[item])
    }
    
    function showKey<K extends keyof T, T> (items: K[], obj: T): Array<T[K]> {
        return items.map(item => obj[item])
    }
    
    function showKey<K extends keyof T, T> (items: K[], obj: {[K in keyof T]: any}): K[] {
        return items.map(item => obj[item])
    }
    
    function showKey<K extends keyof T, T> (items: K[], obj: {[K in keyof T]: any}): Array<K> {
        return items.map(item => obj[item])
    }
    
    let obj = showKey(['name'], testObj)
    

      

    关于TS泛型和高级类型的新发现,持续更新中。。。

  • 相关阅读:
    javascript操作cookie实例
    由浅到深了解JavaScript类[转过来的收藏]
    [MySql识记]create utf8 database
    关于游戏开发中的A*/Astar的寻路算法的问题
    对与list<>泛型的一些操作方法
    浅谈完美时空的小气
    npgsql连接postgresql数据库
    哪个美女最漂亮,自己写的js图片自适应切换
    javascript改变this指针
    [图解] 你不知道的 JavaScript “this”(转)
  • 原文地址:https://www.cnblogs.com/pomelott/p/10548482.html
Copyright © 2011-2022 走看看