• 关于typescript中的extends和infer以及用法


    extends

    extend的意思如果不是用在类的扩展中那么就表示约束在

    type Pick<T, K extends keyof T> = {
      [P in K]: T[P];
    };
    
    • 1
    • 2
    • 3

    比如下面这个例子:
    在这里插入图片描述

    在Picks中K应该约束在keyof T这个类型中。

    infer

    infer表示占位符

    逆变和协变

    协变:子类型赋给父类型
    逆变:父类型赋给子类型(设置strictFunctionTypes:true)
    双向协变:父类型可以赋给子类型,子类型也可以赋给父类型

    {}、Object和object

    {} 是个空对象,没有索引,如果将某些内容注释为{},则可以是任何一种类型。
    object 不能接收原始类型,而 {} 和 Object 都可以,这是它们的区别。
    object 一般会用 Record 代替,约束索引类型更加语义化

    &交叉类型

    合并两个类型

    interface IPerson {
      id: string;
      age: number;
    }
    
    interface IWorker {
      companyId: string;
    }
    
    type IStaff = IPerson & IWorker;
    
    const staff: IStaff = {
      id: 'E1006',
      age: 33,
      companyId: 'EXE'
    };
    
    console.dir(staff)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    如果是非对象:

    type res = 1 & string
    
    • 1

    在这里插入图片描述
    此时合并的是never,所以非对象类型合并的必须是同类型

    ??和?可选链

    ??表示不为null并且不为undefined

    const data = {
        name:1
    }
    const dong = data.name ?? 'dog';
    // 编译后
    "use strict";
    var _a;
    const data = {
        name: 1
    };
    const dog = (_a = data.name) !== null && _a !== void 0 ? _a : 'dog';
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    可以看到是表示不为null和undefined才会获取data.name否则是’dog’。
    ?表示为null或者是undefined和??刚好相反

    const data = {
        name:1
    }
    const dong = data?.name;
    // 编译后
    "use strict";
    const data = {
        name: 1
    };
    const dong = data === null || data === void 0 ? void 0 : data.name;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    可以看到?首先判断是不是null或者undefined,如果是则返回undefined,否则返回data.name。

    keyof any

    在这里插入图片描述

    infer和extends

    infer只能在extends关键字为true的一侧
    infer x可以理解成一个未知数x,表示待推断的函数参数

    type Test<T> = T extends (infer X)[] ? X : never;
    // a类型为number | string
    let a: Test<number[] | string[]> = '10'
    
    • 1
    • 2
    • 3

    接下来带大家分析一个比较好的例子:

    type ParseQueryString<Str extends string>
      = Str extends `${infer Param}&${infer Rest}`
      // Param--a=1  Rest--b=2&c=3 // { a:1 }
      ? MergeParams<ParseParam<Param>, ParseQueryString<Rest>>
      : ParseParam<Str>;
    
    // 将a=1这种格式解析为对象{a:1}
    type ParseParam<Param extends string> =
      // a=1  Key--a  Value--1 // { a:1 }
      Param extends `${infer Key}=${infer Value}`
      ? {
        [K in Key]: Value
      } : Record<string, any>;
    
    // {a:1} {b:2,c:3} 用所有的key做对象,如果只是在其中一个对象那么就直接返回,否则合并两个对象的值
    type MergeParams<
      OneParam extends Record<string, any>,
      OtherParam extends Record<string, any>
    > = {
        // ['a','b','c']
        readonly [Key in keyof OneParam | keyof OtherParam]:
        // 'a' 约束在{a:1}
        Key extends keyof OneParam
        // 'a'是否约束在{b:2,c:3}
        ?
        (Key extends keyof OtherParam
          // 如果'a'同时约束在{a:1}和{b:2,c:3}那么就合并值返回一个列表
          ? MergeValues<OneParam[Key], OtherParam[Key]>
          // 否则返回{a:1}中的1
          : OneParam[Key])
        :
        // 'a'是否约束在{b:2,c:3}中,在就取出值否则不返回
        (Key extends keyof OtherParam
          ? OtherParam[Key]
          : never)
      }
    
    
    type MergeValues<One, Other> =
      // 两个一样
      One extends Other
      ? One
      // other是个列表
      : Other extends unknown[]
      // 合并列表
      ? [One, ...Other]
      // 直接返回一个列表
      : [One, Other];
    
    
    function parseQueryString<Str extends string>(queryStr: Str): ParseQueryString<Str> {
      if (!queryStr || !queryStr.length) {
        return {} as any;
      }
      const items = queryStr.split('&');
      const queryObj: any = {};
      items.forEach(item => {
        const [key, value] = item.split('=');
        if (queryObj[key]) {
          if (Array.isArray(queryObj[key])) {
            queryObj[key].push(value);
          } else {
            queryObj[key] = [queryObj[key], value]
          }
        } else {
          queryObj[key] = value;
        }
      });
      return queryObj
    }
    
    const res = parseQueryString('a=1&b=2&c=3')
    console.log(res);
    
    • 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
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73

    在这里插入图片描述
    使用infer实现递归:

    type ReverseStr<
      Str extends string,
      Result extends string = ''
    > = Str extends `${infer First}${infer Rest}`
      ? ReverseStr<Rest, `${First}${Result}`>
      : Result;
    
    const a = 'hello'
    type b = ReverseStr<typeof a>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    在这里插入图片描述

    /*
    Str = hello Result = '' First = h Rest = ello
    Str = ello Result = 'h' First = e Rest = llo
    Str = llo Result = 'eh' First = l Rest = lo
    Str = lo Result = 'leh' First = l Rest = o
    Str = o Result = 'lleh' First = o Rest = ''
    Str = '' Result = 'olleh' First = '' Rest = ''
     */
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    下面我们来看看综合案例:

    加法

    type BuildArray<
      Length extends number,
      Ele = unknown,
      Arr extends unknown[] = []
    > = Arr['length'] extends Length
      ? Arr
      : BuildArray<Length, Ele, [...Arr, Ele]>;
    
    type Add<Num1 extends number, Num2 extends number> =
      [...BuildArray<Num1>, ...BuildArray<Num2>]['length'];
    
    type addResult = BuildArray<10>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    减法:

    type Subtract<Num1 extends number, Num2 extends number> = 
    // 模式匹配占取部分值
        BuildArray<Num1> extends [...arr1: BuildArray<Num2>, ...arr2: infer Rest]
            ? Rest['length']
            : never;
    type dResult = Subtract<10,9>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    乘法

    type Mutiply<
        Num1 extends number,
        Num2 extends number,
        ResultArr extends unknown[] = []
    > = Num2 extends 0 ? ResultArr['length']
            : Mutiply<Num1, Subtract<Num2, 1>, [...BuildArray<Num1>, ...ResultArr]>;
    
    type mResult = Mutiply<11,10>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    除法

    type Divide<
        Num1 extends number,
        Num2 extends number,
        CountArr extends unknown[] = []
    > = Num1 extends 0 ? CountArr['length']
            : Divide<Subtract<Num1, Num2>, Num2, [unknown, ...CountArr]>;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    数组长度

    type StrLen<
        Str extends string,
        CountArr extends unknown[] = []
    > = Str extends `${string}${infer Rest}` 
        ? StrLen<Rest, [...CountArr, unknown]> 
        : CountArr['length']
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    大于

    type GreaterThan<
        Num1 extends number,
        Num2 extends number,
        CountArr extends unknown[] = []
        // 是否相等
    > = Num1 extends Num2 
        ? false
        // CountArr长度是否等于Num2
        : CountArr['length'] extends Num2
            ? true
            // CountArr长度是否等于Num1
            : CountArr['length'] extends Num1
                ? false
                // 不断的加1去判断是否和Num1或者Num2相等,如果先和Num2相等,那就说明Num1是大于Num2的
                : GreaterThan<Num1, Num2, [...CountArr, unknown]>;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    过滤

    type FilterString<T> = {
      [Key in keyof T as T[Key] extends string ? Key: never]: T[Key];
    }
    
    • 1
    • 2
    • 3

    as表示重命名,返回 never 代表过滤掉,否则保留。

  • 相关阅读:
    Spring Security整体架构
    LFMCW雷达的距离分辨率
    任正非说:段到段而不是端到端的变革,一定会局部优秀了,而全局灾难了。
    区块链论文一般发表在哪些地方?
    C++中地递增递减运算符和指针
    [附源码]Python计算机毕业设计Django校园招聘微信小程序
    四川思维跳动商务信息咨询有限公司可靠吗?
    WordPress主题开发( 十)之—— 条件标签函数(上)
    千亿市场规模医疗美容行业的水究竟有多浑?
    Exception in thread “main“ java.lang.UnsupportedOperationException解决办法
  • 原文地址:https://blog.csdn.net/qq_35094120/article/details/127938496