最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 用ts类型系统实现斐波那契数列

    正文概述 掘金(歪知特博)   2021-05-02   703

    0x00 我们要做什么

    const fib = (n: number): number => (n <= 1 ? n : fib(n - 1) + fib(n - 2));
    
    for (let i = 0; i < 10; i++) {
      console.log(i, fib(i));
    }
    

    我们获得如下输出, 完结撒花 ✿✿ヽ(°▽°)ノ✿

    用ts类型系统实现斐波那契数列

    用ts类型系统实现斐波那契数列

    然而我们其实真正想这样做↓↓↓, 也就是使用TS Type解决FIbonacci

    import { Fib, Add } from "./fib-type";
    
    type one = Fib<1>;
    
    type zero = Fib<0>;
    
    type Two = Add<one, one>;
    
    type Five = Add<Two, Add<Two, one>>;
    
    type Fib5 = Fib<Five>;
    
    type Fib9 = Fib<9>;
    
    type r0 = Fib<Zero>; // type r0= 0
    
    type r1 = Fib<One>; // type r1 = 1
    
    type r2 = Fib<Two>; // type r2 = 1
    
    type r3 = Fib<3>; // type r3 = 2
    
    type r4 = Fib<4>; // type r4 = 3
    
    type r5 = Fib<5>; // type r5 = 5
    
    type r6 = Fib<6>; // type r6 = 8
    
    type r9 = Fib<9>; // type r9 = 34
    
    type sum = Add<r9, r6>; // type sum = 42
    

    用ts类型系统实现斐波那契数列

    用ts类型系统实现斐波那契数列

    1x00 我们该怎么做

    fib中有基本的比较, 加法, 循环, 所以我们也需要使用类型系统依次实现

    1x01 加法

    为了实现加法, 需要先实现一些工具类型

    // 元组长度
    type Length<T extends any[]> = T["length"];
    
    type one = 1;
    
    // 使用extends实现数字相等的比较
    type a111 = 0 extends one ? true : false; // type a111 = false
    type a112 = 1 extends one ? true : false; // type a112 = true
    

    range的实现是递归实现的

    function range(n, list = []) {
      if (n <= 0) return list.length;
      return range(n - 1, [1, ...list]);
    }
    

    ts的限制, 没有循环, 只能用递归代替循环, 后面会有几个类似的写法, 记住一点, 递归有几个出口, 对象就有几个key, 每个key就是一个条件

    // 创建指定长度的元组, 用第二个参数携带返回值
    
    type Range<T extends Number = 0, P extends any[] = []> = {
      0: Range<T, [any, ...P]>;
      1: P;
    }[Length<P> extends T ? 1 : 0];
    
    // 拼接两个元组
    type Concat<T extends any[], P extends any[]> = [...T, ...P];
    type t1 = Range<3>;
    // type t1 = [any, any, any]
    type Zero = Length<Range<0>>;
    // type Zero = 0
    type Ten = Length<Range<10>>;
    // type Ten = 10
    type Five = Length<Range<5>>;
    // type Five = 5
    type One = Length<Range<1>>;
    

    实现加法就比较容易了, 只需要求两个元组合并后的长度

    type Add<T extends number, P extends number> = Length<
      Concat<Range<T>, Range<P>>
    >;
    
    type Two = Add<One, One>;
    // type Two = 2
    type Three = Add<One, Two>;
    // type Three = 3
    

    但是如何实现减法呢?一般减法和除法都比加法难, 所以我们需要更多的工具

    1x02 比较函数

    一些工具类型

    • Shift:删除第一个元素

    • Append:在元组末尾插入元素

    • IsEmpty / NotEmpty:判断列表为空

      // 去除元组第一个元素 [1,2,3] -> [2,3] type Shift<T extends any[]> = ((...t: T) => any) extends ( _: any, ...Shift: infer P ) => any ? P : [];

      type pp = Shift<[number, boolean, string, Object]>; // type pp = [boolean, string, Object] // 向元组中追加 type Append<T extends any[], E = any> = [...T, E]; type IsEmpty<T extends any[]> = Length extends 0 ? true : false; type NotEmpty<T extends any[]> = IsEmpty extends true ? false : true; type t4 = IsEmpty<Range<0>>; // type t4 = true type t5 = IsEmpty<Range<1>>; // type t5 = false

    逻辑类型

    • And:a && b

      // 逻辑操作 type And<T extends boolean, P extends boolean> = T extends false ? false : P extends false ? false : true; type t6 = And<true, true>; // type t6 = true

      type t7 = And<true, false>; // type t7 = false

      type t8 = And<false, false>; // type t8 = false

      type t9 = And<false, true>; // type t9 = false

    小于等于

    伪代码:

    function dfs(a, b) {
      if (a.length && b.length) {
        a.pop();
        b.pop();
        return dfs(a, b);
      } else if (a.length) {
        a >= b;
      } else if (b.length) {
        b > a;
      }
    }
    // 元组的小于等于   T <= P, 同时去除一个元素, 长度先到0的比较小
    
    type LessEqList<T extends any[], P extends any[]> = {
      0: LessEqList<Shift<T>, Shift<P>>;
      1: true;
      2: false;
    }[And<NotEmpty<T>, NotEmpty<P>> extends true
      ? 0
      : IsEmpty<T> extends true
      ? 1
      : 2];
    
    // 数字的小于等于
    type LessEq<T extends number, P extends number> = LessEqList<
      Range<T>,
      Range<P>
    >;
    
    type t10 = LessEq<Zero, One>;
    // type t10 = true
    type t11 = LessEq<One, Zero>;
    // type t11 = false
    type t12 = LessEq<One, One>;
    // type t12 = true
    

    1x03 减法

    列表减法

    默认大减小, 小减大只需要判断下反着来, 然后加个符号就行了, 这里为了简单没有实现

    // 伪代码
    const a = [1, 2, 3];
    const b = [4, 5];
    const c = [];
    while (b.length !== a.length) {
      a.pop();
      c.push(1);
    }// c.length === a.length - b.lengthconsole.log(c.length);
    
    
    // 元组的减法 T - P, 同时去除一个元素, 长度到0时, 剩下的就是结果, 这里使用第三个参数来携带结果, 每次做一次减法, 向第三个列表里面追加
    type SubList<T extends any[], P extends any[], R extends any[] = []> = {
      0: Length<R>;
      1: SubList<Shift<T>, P, Append<R>>;
    }[Length<T> extends Length<P> ? 0 : 1];
    
    type t13 = SubList<Range<10>, Range<5>>;
    // type t13 = 5
    

    数字减法

    其实只是将数字转成元组后再比较

    // 集合大小不能为负数, 默认大减小
    // 数字的减法
    type Sub<T extends number, P extends number> = {
      0: Sub<P, T>;
      1: SubList<Range<T>, Range<P>>;
    }[LessEq<T, P> extends true ? 0 : 1];
    
    type t14 = Sub<One, Zero>;
    //   type t14 = 1
    type t15 = Sub<Ten, Five>;
    // type t15 = 5
    

    我们有了这些工具后, 就可以将js翻译为ts

    2x00 Fib: JS函数 --> TS类型

    在js中,我们使用函数

    const fib = (n: number): number => n <= 1 ? n : fib(n - 1) + fib(n - 2);
    

    在ts中,我们使用类型, 其实只是换了一种写法, 万变不离其宗~

    export type Fib<T extends number> = {
      0: T;
      1: Add<Fib<Sub<T, One>>, Fib<Sub<T, Two>>>;
    }[LessEq<T, One> extends true ? 0 : 1];
    
    type r0 = Fib<Zero>;
    // type r10= 0
    type r1 = Fib<One>;
    // type r1 = 1
    
    type r2 = Fib<Two>;
    // type r2 = 1
    
    type r3 = Fib<3>;
    // type r3 = 2
    
    type r4 = Fib<4>;
    // type r4 = 3
    
    type r5 = Fib<5>;
    //type r5 = 5
    
    type r6 = Fib<6>;
    //   type r6 = 8
    

    我还有很多好玩的想法, 可惜这里地(dian)方(zan)太少写不下了, 你懂得 \(^o^)/~

    用ts类型系统实现斐波那契数列

    其他好玩的

    TypeScript类型元编程:实现8位数的算术运算

    TypeScript 4.1 新特性:字符串模板类型,Vuex 终于有救了?

    广告

    字节前端-教育方向, 想来玩的话可以内推

    要是有好玩的岗位, 也可以把我带走... (打杂, 搬砖的就算了)


    起源地下载网 » 用ts类型系统实现斐波那契数列

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元