美文网首页前端修仙之路
为vue3学点typescript, 解读高级类型

为vue3学点typescript, 解读高级类型

作者: 铁皮饭盒 | 来源:发表于2019-07-31 08:54 被阅读0次

    直达

    第一课, 体验typescript

    第二课, 基础类型和入门高级类型

    第三课, 泛型

    第四课, 解读高级类型

    回顾

    第二课的时候为了更好的讲解基础类型, 所以我们讲解了一部分高级类型, 比如"接口( interface )" / "联合类型( | )" / "交叉类型( & )", 本节课我会把剩余高级类型都讲完.

    知识点摘要

    本节课主要关键词为: 自动类型推断 / 类型断言 / 类型别名(type) / 映射类型(Pick/Record等...) / 条件类型(extends) / 类型推断(infer)

    自动类型推断(不用你标类型了,ts自己猜)

    第二课我们讲了那么多基础类型, 大家现在写ts的时候一定会在每个变量后面都加上类型吧? 但是?

    现在告诉大家有些情况下你不需要标注类型, ts可以根据你写的代码来自动推断出类型:

    赋值字面量给变量
    
    let n = 1; // ts会自动推断出n是number类型
    n+=3 // 不报错,因为已知类型
    
    let arr1 = []; // 类型为: any[]
    arr1.push(1,2,{o:3}); 
    
    let arr = [1]; // 内部要有数字, 才能推断出正确类型
    arr.push(2);
    
    
    自动阅读if条件判断
    let n: number|null = 0.5 < Math.random() ? 1:null;
    if(null !== n){
        n+=3 // ts知道现在n不是null是number
    }
    
    浏览器自带api
    document.ontouchstart = ev=>{ // 能自动推断出ev为TouchEvent
        console.log(ev.touches);  // 不报错, TouchEvent上有touches属性
    }
    
    typeof

    typeof就是js中的typeof, ts会根据你代码中出现的typeof来自动推断类型:

    let n:number|string = 0.5 < Math.random()? 1:'1';
    
    // 如果没有typeof, n*=2会报错, 提示没法推断出当前是number类型, 不能进行乘法运算
    if('number' === typeof n) {
        n*= 2;
    } else  {
        n= '2';
    }
    

    注意: 在ts文档中, 该部分的知识点叫做typeof类型保护, 和其他类型推断的内容是分开的, 被写在高级类型/类型保护章节中.

    instanceof

    ts会根据你代码中出现的instanceof来自动推断类型:

    let obj = 0.5 < Math.random() ? new String(1) : new Array(1);
    if(obj instanceof String){
        // obj推断为String类型
        obj+= '123'
    } else {
        // obj为any[]类型
        obj.push(123);
    }
    

    注意: 在ts文档中, 该部分的知识点叫做instanceof类型保护, 和其他类型推断的内容是分开的, 被写在高级类型/类型保护章节中.

    类型断言(你告诉ts是什么类型, 他都信)

    有些情况下系统没办法自动推断出正确的类型, 就需要我们标记下, 断言有2种语法, 一种是通过"<>", 一种通过"as", 举例说明:

    let obj = 0.5 < Math.random() ? 1 : [1]; // number|number[]
    
    // 断言, 告诉ts, obj为数组
    (<number[]>obj).push(1);
    
    //等价
    (obj as number[]).push(1);
    

    类型别名(type)

    类型别名可以表示很多接口表示不了的类型, 比如字面量类型(常用来校验取值范围):

    type A = 'top'|'right'|'bottom'|'left'; // 表示值可能是其中的任意一个
    type B = 1|2|3;
    type C = '红'|'绿'|'黄';
    type D = 150;
    
    let a:A = 'none'; // 错误, A类型中没有'none'
    
    更多组合:
    interface A1{
        a:number;
    }
    type B = A1 | {b:string};
    type C = A1 & {b:string};
    
    // 与泛型组合
    type D<T> = A1 | T[];
    

    索引类型(keyof)

    js中的Object.keys大家肯定都用过, 获取对象的键值, ts中的keyof和他类似, 可以用来获取对象类型的键值:

    type A = keyof {a:1,b:'123'} // 'a'|'b'
    type B = keyof [1,2] // '1'|'2'|'push'... , 获取到内容的同时, 还得到了Array原型上的方法和属性(实战中暂时没遇到这种需求, 了解即可)
    

    可以获得键值, 也可以获取对象类型的值的类型:

    type C = A['a'] // 等于type C = 1;
    let c:C = 2 // 错误, 值只能是1
    

    映射类型(Readonly, Pick, Record等...)

    映射类型比较像修改类型的工具函数, 比如Readonly可以把每个属性都变成只读:

    type A  = {a:number, b:string}
    type A1 = Readonly<A> // {readonly a: number;readonly b: string;}
    

    打开node_modules/typescript/lib文件夹可以找到lib.es5.d.ts, 在这我们能找到Readonly的定义:

    type Readonly<T> = {
        readonly [P in keyof T]: T[P];
    };
    

    其实不是很复杂, 看了本节课前面前面的内容, 这个很好理解是吧:

    1. 定义一个支持泛型的类型别名, 传入类型参数T.
    2. 通过keyof获取T上的键值集合.
    3. in表示循环keyof获取的键值.
    4. 添加readonly标记.
    Partial<T>, 让属性都变成可选的
    type A  = {a:number, b:string}
    type A1 = Partial<A> // { a?: number; b?: string;}
    
    Required<T>, 让属性都变成必选
    type A  = {a?:number, b?:string}
    type A1 = Required<A> // { a: number; b: string;}
    
    Pick<T,K>, 只保留自己选择的属性, U代表属性集合
    type A  = {a:number, b:string}
    type A1 = Pick<A, 'a'> //  {a:number}
    
    Omit<T,K> 实现排除已选的属性
    type A  = {a:number, b:string}
    type A1 = Omit<A, 'a'> // {b:string}
    
    Record<K,T>, 创建一个类型,T代表键值的类型, U代表值的类型
    type A1 = Record<string, string> // 等价{[k:string]:string}
    
    Exclude<T,U>, 过滤T中和U相同(或兼容)的类型
    type A  = {a:number, b:string}
    type A1 = Exclude<number|string, string|number[]> // number
    
    // 兼容
    type A2 = Exclude<number|string, any|number[]> // never , 因为any兼容number, 所以number被过滤掉
    
    Extract<T,U>, 提取T中和U相同(或兼容)的类型
    type A  = {a:number, b:string}
    type A1 = Extract<number|string, string|number[]> // string
    
    NonNullable<T>, 剔除T中的undefined和null
    type A1 = NonNullable<number|string|null|undefined> // number|string
    
    ReturnType<T>, 获取T的返回值的类型
    type A1= ReturnType<()=>number> // number
    
    InstanceType<T>, 返回T的实例类型

    ts中类有2种类型, 静态部分的类型和实例的类型, 所以T如果是构造函数类型, 那么InstanceType可以返回他的实例类型:

    interface A{
        a:HTMLElement;
    }
    
    interface AConstructor{
        new():A;
    }
    
    function create (AClass:AConstructor):InstanceType<AConstructor>{
        return new AClass();
    }
    
    Parameters<T> 获取函数参数类型

    返回类型为元祖, 元素顺序同参数顺序.

    interface A{
        (a:number, b:string):string[];
    }
    
    type A1 = Parameters<A> // [number, string]
    
    ConstructorParameters<T> 获取构造函数的参数类型

    Parameters类似, 只是T这里是构造函数类型.

    interface AConstructor{
        new(a:number):string[];
    }
    
    type A1 = ConstructorParameters<AConstructor> // [number]
    

    extends(条件类型)

    T extends U ? X : Y
    

    用来表示类型是不确定的, 如果U的类型可以表示T, 那么返回X, 否则Y. 举几个例子:

    type A =  string extends '123' ? string :'123' // '123'
    type B =  '123' extends string ? string :123 // string
    

    明显string的范围更大, '123'可以被string表示, 反之不可.

    infer(类型推断)

    单词本身的意思是"推断", 实际表示在extends条件语句中声明待推断的类型变量. 我们上面介绍的映射类型中就有很多都是ts在lib.d.ts中实现的, 比如Parameters:

    type Parameters<T extends (...args: any) => any> = T extends (...args: infer P) => any ? P : never;
    

    上面声明一个P用来表示...args可能的类型, 如果(...args: infer P)可以表示 T, 那么返回...args对应的类型, 也就是函数的参数类型, 反之返回never.

    注意: 开始的T extends (...args: any) => any用来校验输入的T是否是函数, 如果不是ts会报错, 如果直接替换成T不会有报错, 会一直返回never.

    应用infer

    接下来我们利用infer来实现"删除元祖类型中第一个元素", 这常用于简化函数参数, 这有一个我之前的应用

    export type Tail<Tuple extends any[]> = ((...args: Tuple) => void) extends ((a: any, ...args: infer T) => void) ? T : never;
    

    总结

    多写多练, 很快就上手, 放几个我用ts写的项目当做参考, 抛砖引玉, 加油!

    手势库: https://github.com/any86/any-touch

    命令式调用vue组件: https://github.com/any86/vue-create-root

    工作中常用的一些代码片段: https://github.com/any86/useful-utils

    一个mini的事件管理器: https://github.com/any86/any-event

    相关文章

      网友评论

        本文标题:为vue3学点typescript, 解读高级类型

        本文链接:https://www.haomeiwen.com/subject/zewarctx.html