新聞中心
本文轉載自微信公眾號「DYBOY」,作者DYBOY。轉載本文請聯(lián)系DYBOY公眾號。

創(chuàng)新互聯(lián)于2013年開始,先為定日等服務建站,定日等地企業(yè),進行企業(yè)商務咨詢服務。為定日企業(yè)網站制作PC+手機+微官網三網同步一站式服務解決您的所有建站問題。
作為一名前端開發(fā)者,一定知道TypeScript經常被用于項目中的類型約束,使得在JavaScript這種弱類型語言中有了靜態(tài)檢查的能力,也推進了前端工程化的演進速度,在研究學習TypeScript過程中,我的小伙伴發(fā)現了TS的一些好玩兒功能,獨樂樂不容眾樂樂,遂分享這篇文章給大家。
小伙伴(育豪)的原文可能理解起來有一些難度,筆者有嘗試增加一些描述,但想要完全領略TS的“類型體操”的奧妙,還是得實操一番。
一、我們要做什么
我們的目的是想要通過TypeScript的類型聲明式語法,編程實現一個斐波那契數列算法。換句話說,類似于用現有的機器碼到指令集、二進制到十進制、匯編語言到高級編程語言的過程,讓類型定義語法也可以實現編程。
最終我們要實現的斐波那契數列代碼是這樣的?
- 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));
- }
運行結果如下:
斐波那契數列打印結果
程序完全沒問題,完結撒花!
開玩笑的,上面是只一個用了TypeScript類型定義的JavaScript寫法,我們其實真正想這樣做↓↓↓, 也就是使用TS Type解決FIbonacci
- import { Fib, Add } from './fib-type';
- type one = Fib<1>;
- type zero = Fib<0>;
- type Two = Add
; - type Five = Add
>; - type Fib5 = Fib
; - type Fib9 = Fib<9>;
- type r0 = Fib
; // type r0= 0 - type r1 = Fib
; // type r1 = 1 - type r2 = Fib
; // 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
; // type sum = 42
類型提示
二、我們該怎么做
要想實現斐波那契數列,參考一開始的代碼,有基本的比較, 加法, 循環(huán)語法, 所以我們也需要使用類型系統(tǒng)依次實現這三種功能
2.1 加法的實現
為了實現加法, 需要先實現一些工具類型
- // 元組長度
- type Length
= 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])
- }
TypeScript的限制, 沒有循環(huán), 只能用遞歸代替循環(huán), 后面會有幾個類似的寫法, 記住一點:遞歸有幾個出口, 對象就有幾個 key, 每個 key 就是一個條件
- // 創(chuàng)建指定長度的元組, 用第二個參數攜帶返回值
- type Range
= { - 0: Range
; - 1: P;
- }[Length
extends T ? 1 : 0];
- // 拼接兩個元組
- type Concat
= [...T, ...P]; - type t1 = Range<3>;
- // type t1 = [any, any, any]
- type Zero = Length
>; - // type Zero = 0
- type Ten = Length
>; - // type Ten = 10
- type Five = Length
>; - // type Five = 5
- type One = Length
>;
有了上面的工具語法,那么實現加法就比較容易了, 只需要求兩個元組合并后的長度
- type Add
= Length< - Concat
, Range >
- >;
- type Two = Add
; - // type Two = 2
- type Three = Add
; - // type Three = 3
有了加法,該如何實現減法呢?一般減法和除法都比加法難, 所以我們需要更多的工具類型函數!
2.2 工具函數
2.2.1 實現一些基本工具類型
- Shift:刪除第一個元素
- Append:在元組末尾插入元素
- IsEmpty / NotEmpty:判斷列表為空
- // 去除元組第一個元素 [1,2,3] -> [2,3]
- type Shift
= ((...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, E]; - type IsEmpty
= Length extends 0 ? true : false; - type NotEmpty
= IsEmpty extends true ? false : true; - type t4 = IsEmpty
>; - // type t4 = true
- type t5 = IsEmpty
>; - // type t5 = false
2.2.2 邏輯類型
- And:a && b
- // 邏輯操作
- type And
= T extends false - ? false
- : P extends false
- ? false
- : true;
- type t6 = And
; - // type t6 = true
- type t7 = And
; - // type t7 = false
- type t8 = And
; - // type t8 = false
- type t9 = And
; - // type t9 = false
2.2.3 小于等于
偽代碼: 主要思想是同時從列表中取出一個元素, 長度先到0的列表比較短
- 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
= { - 0: LessEqList
, Shift >;
- 1: true;
- 2: false;
- }[And
, NotEmpty > extends true
- ? 0
- : IsEmpty
extends true - ? 1
- : 2];
- // 數字的小于等于
- type LessEq
= LessEqList , Range >;
- type t10 = LessEq
; - // type t10 = true
- type t11 = LessEq
; - // type t11 = false
- type t12 = LessEq
; - // type t12 = true
2.3 減法的實現
減法有兩個思路,列表長度相減求值和數字相減求值
2.3.1 列表減法
默認大減小, 小減大只需要判斷下反著來, 然后加個符號就行了, 這里為了簡單沒有實現,可參考偽代碼如下:
- // 偽代碼
- 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
= { - 0: Length
; - 1: SubList
, P, Apped >; - }[Length
extends Length ? 0 : 1];
- type t13 = SubList
, Range<5>>; - // type t13 = 5
2.3.2 數字減法
思想:將數字轉成元組后再比較
- // 集合大小不能為負數, 默認大減小
- // 數字的減法
- type Sub
= { - 0: Sub
;
- 1: SubList
, Range >;
- }[LessEq
extends true ? 0 : 1]; - type t14 = Sub
; - // type t14 = 1
- type t15 = Sub
; - // type t15 = 5
我們有了這些工具后, 就可以將一開始用JavaScript實現的斐波那契數列的實現代碼,翻譯為TypeScript類型編碼
三、Fib: JS函數 --> TS類型
在JavaScript中,我們使用函數
- const fib = (n: number): number => n <= 1 ? n : fib(n - 1) + fib(n - 2);
在TypeScript中,我們使用類型, 其實只是換了一種寫法, 用類型函數描述運算, 萬變不離其宗~
由于TypeScript遞歸限制, 并不能求解非常大的項, 不過好玩就完事了~
- export type Fib
= { - 0: T;
- 1: Add
>, Fib>>; - }[LessEq
extends true ? 0 : 1]; - type r0 = Fib
; - // type r10= 0
- type r1 = Fib
; - // type r1 = 1
- type r2 = Fib
; - // 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
最后,推薦一些其他好玩的項目:
- 《TypeScript類型元編程:實現8位數的算術運算》 - https://zhuanlan.zhihu.com/p/85655537
- 《TypeScript 4.1 新特性:字符串模板類型,Vuex 終于有救了?》 - https://juejin.cn/post/6867785919693832200
- 《Ts 類型系統(tǒng)實現線性查找》 - https://bytedance.feishu.cn/docs/doccney0oWRZMSM1w9e0izshW5d
四、總結
看了TypeScript實現斐波納切數列這一套操作有沒有讓你有體驗到重回“實現流水線CPU”的實驗室時光?
IT在最近幾十年的發(fā)展突飛猛進,越來越多的“程序員”加入到了互聯(lián)網行業(yè),在一些高級語言以及開發(fā)框架下,“程序員”的編碼也只需要關注業(yè)務邏輯實現,很少有人會再去關注計算機底層是怎么實現加減乘除的,當然社會在進步,技術也在日新月異地迭代,偶爾駐足,回憶剛接觸計算機編程,在命令行輸出第一行“Hello World!”代碼那時欣喜的自己,也許那是我們都回不去的青春...
網頁標題:用TypeScript類型系統(tǒng)編程實現斐波那契數列
路徑分享:http://www.dlmjj.cn/article/ccdohpg.html


咨詢
建站咨詢
