TypeScript 原來可以這么香?!

先問一個問題,JavaScript有幾種數據類型?

numberstringbooleannullundefinedsymbolbigintobject

其中 bigint 是 ES2020 新增的數據類型,而早在 TS3.2 時便成為 TS 的標準,其實還有好多 ES+ 標準是 TS 率先提出的,可見 TS 在很多方面是走在了 ES 前列。

TypeScript又新增了多少種數據類型?

anyunknownenumvoidnevertuple...

其實 TypeScript 更重要的是通過 interface 和 type 賦予了用戶自定義數據類型的能力,使數據在流轉的過程中始終能輕易被用戶掌握。

Bug 絞殺者

TypeScript 可以讓我們的程序寫的更健壯,并且更好維護,豐富的代碼的提示功能也能夠提高我們的開發效率以及降低協作成本,并幫助我們在程序的編譯階段檢查出許多因為類型的原因導致的低級錯誤。

那么我們先來看下 JavaScript 項目最常見的十大錯誤:

這些低級的錯誤是不是耳熟能詳?為了解決這些問題,占用了我們大量的 debug 和 google 的時間。程序員最煩的兩件事:一件是自己寫完代碼還要寫注釋文檔,一件是別人的代碼沒留下任何注釋文檔。

在我們的日常開發中,很常見的是調用或者修改別人寫的函數。但是如果別人的代碼沒留下任何注釋的話,我們就要硬著頭皮去看里面的邏輯。

假如我們優化了一個底層類庫的參數類型,而不知道有多少處引用,在提交代碼前,是不是內心在打鼓,心里沒底呢?畢竟我們都不想被殺了祭天吧...

筆者前不久就因為一個很小的問題 debug 了很長時間,我們來看一下,因內部代碼所以經過脫敏處理。

因為 JavaScript 太靈活了,它允許你對參數做任何操作,所以之前同學寫的時候直接將 userId 掛載到入參 query 上面,后面用到處解構也就有了相應的 userId 屬性。但是筆者對這部分業務不熟悉,以為入參 query 不會改變,所以 debug 了好久。

而假如我們使用的是 TypeScript ,我們會定義好入參 query 的結構,為 query 添加 userId 屬性的時候就能有報錯提示,可以規避我們以前的寫法,換成更健壯性的寫法。

另外 TypeScript 還能實現自文檔化,使后面維護的同學也能輕松的接手。

我們可以通過/** */來注釋 TypeScript 的類型,當我們在使用相關類型的時候就會有注釋的提示,這個技巧可以幫助我們節約翻文檔或者跳頁看注釋的時間,在團隊內合理的推廣使用,能夠極大的提高我們的開發效率。

優化程序性能

合理的使用 TypeScript ,再憑借 V8 引擎可以幫助我們極大的優化程序的性能。

在 V8 引擎下,引入了 TurboFan 編譯器,它會在特定的情況下進行優化,將代碼編譯成執行效率更高的 Machine Code,這個編譯器雖然不是 JavaScript 必須的,但是卻能夠極大的提高代碼執行效能。

我們知道, JavaScript 代碼首先會解析為抽象語法樹(AST),然后會通過解釋器或者編譯器轉化為 Bytecode 或者 Machine Code。其中 Ignition 負責將 AST 轉化為 BytecodeTurboFan 負責編譯出優化后的 Machine Code,并且 Machine Code 在執行效率上優于 Bytecode

那么問題來了,什么情況下的代碼會編譯為 Machine Code 呢?JavaScript 是一門動態類型語言,并且有一大堆的隱式類型轉換規則,比如數字相加、字符串相加、對象和字符串相加等等。這樣的情況也就勢必導致了內部要增加很多的判斷邏輯,降低運行時的效率。

function test (x) {return x + x;
}test(1)
test(2)
test(3)
test(4)

對于上面這段代碼來說,如果一個函數被多次調用并且參數一直傳入 number 類型,那么 V8 引擎就會認為該段代碼可以編譯為 Machine Code,因為我們固定了類型,不需要再執行很多判斷邏輯了。

但是一旦我們傳入的參數類型改變,那么 Machine Code 就會被 DeOptimized Bytecode,這樣就會造成性能上的損耗。所以如果我們希望代碼能盡可能多的編譯為 Machine Code 并且 DeOpimized 的次數減少,那么我們就應該盡可能的保證傳入的類型一致。

但是你可能還有一個疑問,優化前后的性能提升到底是怎么樣的呢?有什么數據支撐么?

const v8 = require('v8-natives');
const { performance, PerformanceObserver } = require('perf_hooks')function test(x, y) {return x + y
}const obs = new PerformanceObserver((list, observer) => {console.log(list.getEntries())observer.disconnect()
})
obs.observe({ entryTypes: ['measure'], buffered: true })performance.mark('start')let number = 10000000// 不優化代碼
v8.neverOptimizeFunction(test)while (number--) {test(1, 2)
}performance.mark('end')
performance.measure('test', 'start', 'end')

我們接下來使用 performance 這個 API 測量一下代碼的執行時間,這個 API 經常用于一些性能測試,還可以用來測量各種網絡連接中的時間消耗,并且也可以在瀏覽器中使用。

我們實際運行一下代碼發現,經過優化后的代碼執行時間只需要 10ms,但是不優化的代碼卻是前者的十二倍,高達了 124ms

在這個案例中,我們能夠看到 V8 的性能優化十分強大,只需要我們符合一定規則書寫代碼,引擎底層就能幫助我們自動優化代碼。

那么為了讓 V8 優化代碼,我們要盡可能的保證傳入參數的類型一致,而這,也正是 TypeScript 帶給我們的好處之一,借助 TypeScript,可以強迫我們思考定義好每一處的變量類型,讓每一處的變量類型都做到最小可控。使 V8 可以自動將我們的代碼優化成 Machine Code。

所以我們可以設想一下,未來怎么樣憑借 TypeScript,讓 V8 進一步得到優化。

  • 1、使用 TypeScript 編程,遵循嚴格的類型化編程規則,摒棄 AnyScript。

    2、構建的時候將 TypeScript 直接編譯為 Bytecode,而不是 JavaScript 文件,這樣運行的時候就省去了 Parse 以及生成 Bytecode 的過程。

    3、運行的時候,需要先將 Bytecode 編譯為對應 CPU 的匯編代碼。

    這樣由于采用了類型化的編程方式,有利于編譯器優化所生成的匯編代碼,省去了很多額外的操作。

未來的潮流

很多的前端底層庫都在從 JavaScript 向 TypeScript 遷移,像我們熟悉的 Angular 和 Vue3 已經全面用 TypeScript 重構代碼,在 ECMAScript 標準推出靜態類型檢查之前,TypeScript 是解決當下問題的最佳實踐。

stackoverflow 統計的2020年最受開發者喜歡的語言,TypeScript 已排到第二名。

但是很多同學一開始可能不太喜歡使用 TypeScript,人們在接觸一個新事物的時候往往會出現本能的抗拒,因為不太確定這個新的事物能夠帶給我們什么開發體驗的提升。

可能有些同學抱著試試看的態度去嘗試使用了一下,但卻發現這個東西巨難用,常常代碼死活編譯不過去,各種類型不匹配。一些底層類庫的不完善,沒有很好的支持 TypeScript 也會增加我們開發的上手難度。

這也難怪,因為 JavaScript 是一門動態弱類型語言,對變量的類型非常寬容,而且不會在這些變量和它們的調用者之間建立結構化的契約,JavaScript 帶給我們極大的靈活性。

一想到以前快樂的時光,可能有的同學直接棄療,重新回到 JavaScript 的懷抱;可能有的同學轉而去寫 AnyScript,遇事不決上 any,但這是非常差的編程習慣!假如我們寫 AnyScript,我們就失去了 TypeScript 的意義,為此我們還要多寫好多的冗余代碼,影響開發效率。

其實使用 TypeScript 進行開發,理論上我們99.99%的情況都不應該使用 any,當我們想要使用 any 的時候,就多去思考,查閱資料,仔細推敲,只要我們能夠不斷忍住不寫 any,多寫 TypeScript,寫到如臂指使,后面你會發現自己的開發效率在不斷提升。

實用小技巧

1. 巧用 keyof

假如我們要實現一個 getValue 函數,作用是根據傳入的 object 和 key 獲取 object 上 key 所對應的屬性值 value,一開始的 JavaScript 版本可能是這樣的。

const data = {a: 3,hello: 'world'
}
function getValue(o, name) {return o[name]
}

可以看到這就是 JavaScript 的弊端,我們對傳入的 o 和 name 都沒有約束,對這一切都是未知的,人的恐懼就源自對未知事物的不確定性。尤其如果這是一個底層類庫,我們不知道有多少人去調用,那這個風險就更加增大了,所以我們可能用 TypeScript 這樣去改造它。

function getValue(o: any, name: string) {return o[name]
}

但這樣寫貌似也有很多問題,函數返回值的類型變成了 any,失去了 TypeScript 類型校驗的功能,讓類型重新變的不可控,name 類型固然是 string,但還是太寬泛了,實際上,name 的值只能是o的鍵的并集,而且如果我們將 name 拼寫錯誤,TypeScript 也不會幫我們報錯提示。這個時候我們可以使用 keyof 結合泛型來加強 getValue 函數的類型功能。

function getValue<T extends object, K extends keyof T>(o: T, name: K): T[K] {return o[name]
}

2. 接口智能提示

interface Seal {name: string;url: string;
}
interface API {"/user": { name: string; age: number; phone: string };"/seals": { seal: Seal[] };
}
const api = <URL extends keyof API>(url: URL): Promise<API[URL]> => {return fetch(url).then((res) => res.json());
};

借助泛型以及泛型約束,我們可以實現智能提示的功能,不光接口名可以智能提示,接口返回也可以智能提示。當我們輸入 api 的時候,其會自動將 API interface 下的所有 key 提示給我們,當我們輸入某一個 key 的時候,其會根據 key 命中的 interface 定義的類型,然后給予類型提示。這在統一接口管理方面有很大的用處,可以幫助我們面向接口編程。

3. 巧用類型保護

interface User {name: string;age: number;occupation: string;
}
interface Admin {name: string;age: number;role: string;
}
export type Person = User | Admin;
export const persons: Person[] = [{name: 'Max Mustermann',age: 25,occupation: 'Chimney sweep'},{name: 'Jane Doe',age: 32,role: 'Administrator'},{name: 'Kate Müller',age: 23,occupation: 'Astronaut'},{name: 'Bruce Willis',age: 64,role: 'World saver'}
];
export function logPerson(person: Person) {let additionalInformation: string;if (person.role) {additionalInformation = person.role;} else {additionalInformation = person.occupation;}console.log(` - ${person.name}, ${person.age}, ${additionalInformation}`);
}
persons.forEach(logPerson);

我們可以看到,當我們定義了兩種 Person 類型:User 和 Admin,而在使用的時候是比較寬泛的 Person,那我們就不能直接使用 User 或者 Admin 的特有屬性 role 或者 occupation。因為 TypeScript 沒有足夠的信息確定 Person 究竟是 User 還是 Admin。

一種方法是使用類型斷言,顯示的告訴 TypeScript,person 就是 Admin 類型或者就是 User 類型,但是這樣做一方面不夠優雅,要在每一處都加上斷言;另一方面濫用斷言也會讓我們的代碼變得不可控,不能讓 TypeScript 幫助我們進行合理的類型推斷。像雙重斷言可以規避掉 TypeScript 的類型檢查機制也是與 any 一樣,要盡可能去避免的。

正確的做法是使用類型收縮,比如使用 is,in,typeof,instanceof 等,使得 TypeScript 能夠 get 到當前的類型,假如 person 上面有 role 屬性,TypeScript 就可以推斷出 person 就是 Admin 類型,創建類型保護區塊,在當前的代碼塊按照 Admin 類型處理,代碼也簡潔了很多。

同樣是這個例子,我們再改造一下,通過兩個函數來判斷 person 的具體類型是 Admin 還是 User。但是很不幸,TypeScript 依然不能很智能的知道 person 在第一個代碼塊里是 Admin 類型,在第二個代碼塊里是 User 類型。

這個時候我們就要改造一下 isAdmin 和 isUser 的函數返回,創建用戶自定義的類型保護函數,顯式的告訴 TypeScript,函數返回為 true 時,指定 person 的類型確定為 Admin 或者 User,這樣 TypeScript 就知道 person 的確定類型了,這就是類型位詞。

4.常用的高級類型

這其實涉及到了類型編程到概念,簡而言之,我們平時寫代碼是對值進行編程,而類型編程是對類型進行編程,可以利用 keyof 對屬性做一些擴展,省的我們要重新定義一下接口,造成很多冗余代碼。

這些高級類型在日常編程中有非常廣泛的使用,尤其 Partial 可以將所有的屬性變成可選的,如在我們日常的搜索邏輯,我們可以根據單一條件搜索,也可以根據組合條件搜索。Omit 可以幫助我們復用一個類型,但是又不需要此類型內的全部屬性,當父組件通過 props 向下傳遞數據的時候,可以剔除一些無用的類型。

Record 也是一個比較常用的高級類型,可以幫助我們從 Union 類型中創建新類型,Union 類型中的值用作新類型的屬性。當我們拼寫錯誤,或者漏寫一些屬性,或者加入了沒有預先定義的屬性進去,TypeScript 都可以給我們很友好的報錯提示。

type Partial<T> = {[P in keyof T]?: T[P];
};
type Required<T> = {[P in keyof T]-?: T[P];
};
type Pick<T, K extends keyof T> = {[P in K]: T[P];
};
type Exclude<T, U> = T extends U ? never : T;
// 相當于: type A = 'a'
type A = Exclude<'x' | 'a', 'x' | 'y' | 'z'>
type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;
type Record<K extends keyof any, T> = {[P in K]: T;
};interface User {id: number;age: number;name: string;
};
// 相當于: type PartialUser = { id?: number; age?: number; name?: string; }
type PartialUser = Partial<User>
// 相當于: type PickUser = { id: number; age: number; }
type PickUser = Pick<User, "id" | "age">
// 相當于: type OmitUser = { age: number; name: string; }
type OmitUser = Omit<User, "id">type AnimalType = 'cat' | 'dog' | 'frog';
interface AnimalDescription { name: string, icon: string }
const AnimalMap: Record<AnimalType, AnimalDescription> = {cat: { name: '貓', icon: ' '},dog: { name: '狗', icon: ' ' },forg: { name: '蛙', icon: ' ' }, // Hey!
};

5.巧用類型約束

在 .tsx 文件中,泛型可能會被當作 jsx 標簽

const toArray = <T>(element: T) => [element]; // Error in .tsx file.

加 extends 可破

const toArray = <T extends {}>(element: T) => [element]; // No errors.

TypeScript 還可以給一些缺乏類型定義的第三方庫定義類型,找到一些沒有 d.ts 聲明的開源庫,為開源社區貢獻聲明文件。

學習參考

  1. https://www.typescriptlang.org/docs/handbook/release-notes/overview.html 官方各個版本文檔

  2. https://github.com/microsoft/TypeScript/projects/9 官方討論

  3. https://github.com/microsoft/vscode VS Code 是 TypeScript 編寫的,毫無疑問也是學習的好地方

  4. https://basarat.gitbook.io/typescript/getting-started TypeScript Deep Dive

  5. https://github.com/typescript-exercises/typescript-exercises TypeScript 優秀的練習題

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/275903.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/275903.shtml
英文地址,請注明出處:http://en.pswp.cn/news/275903.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

java8新特性stream深入解析

2019獨角獸企業重金招聘Python工程師標準>>> 繼續java8源碼的發燒熱&#xff0c;越看越是有充實的感覺。 數據時代下的產物 Java順應時代的發展推出的高效處理大量數據能力的api&#xff0c;它專注于對集合對象進行各種非常便利、高效的聚合操作&#xff0c;借助于同…

mysql內連接的自連接_mysql 內連接、外連接、自連接

一)內連接(等值連接)&#xff1a;查詢客戶姓名&#xff0c;訂單編號&#xff0c;訂單價格---------------------------------------------------select c.name,o.isbn,o.pricefrom customers c inner join orders owhere c.id o.customers_id;-------------------------------…

關于ASP.NET MVC

我是否要學習一下ASP.NET MVC呢&#xff1f;因爲從它剛發布的時候就已經初步的學習了一下&#xff0c;可是一直沒有堅持下來。不過心里對于這份惦記&#xff0c;又讓我始終放不下&#xff0c;看來應該抽個時間來系統的學習一下。 就這樣吧&#xff0c;把自己的博客當成微博來使…

版式設計與創意 pdf_戀愛與版式

版式設計與創意 pdfSince its beginnings, Libe?ration has been characterized by a very distinctive use of typeface, to such an extent that Libe? has put its mark on fonts from across different eras, appropriating these in a certain way.小號因斯它的起點&…

移動網站開發——標記語言

移動互聯網被稱為“第五次科技革命”&#xff0c;而隨著iPhone和Android等智能手機的日漸流行和iPad等平板電腦的出現&#xff0c;移動互聯網的潛力和趨勢也愈發顯現&#xff0c;針對移動設備的網站開發越來越受到關注&#xff0c;國內很多公司也開始重視面向所有移動設備的網站…

mysql適配器_MySQL適配器PyMySQL詳解

import pymysqlimport datainfoimport time#獲取參數host datainfo.hostusername datainfo.usernamepassword datainfo.passworddatabase datainfo.dbprint()#測試數據庫連接def testconnect():#打開數據庫鏈接db pymysql.connect(host,username,password,database)#使用c…

獲取當前Tomcat實例的端口

有時需要在當前代碼中獲取當前Server實例的端口號, 通過HttpServletRequest請求可以, 但有時也需要在沒有請求的情況下獲取到端口號. 用以下方法是可以獲取到的: public int getHttpPort() {try {MBeanServer server;if (MBeanServerFactory.findMBeanServer(null).size() >…

前端技術未來三年前瞻性思考

大家好&#xff0c;我是若川。今天推薦云謙大佬的一篇好文章&#xff0c;值得收藏。點擊下方卡片關注我、加個星標&#xff0c;或者查看源碼等系列文章。學習源碼整體架構系列、年度總結、JS基礎系列習慣從業務場景、用戶體驗、研發速度、維護成本四個維度來看框架等前端技術&a…

微信臨時素材接口_在接口中表達臨時性

微信臨時素材接口When interacting with today’s graphic user interfaces (GUI), we experience a sense of realism. As of now, certain aspects of realism (for example animations) create the appearance that user interface graphics behave in accordance with the …

程序員,當你寫程序寫累了怎么辦。

記得泡泡網的CEO李想說過這樣一句話&#xff0c;大體就是&#xff1a;做一件事情&#xff0c;一開始是興趣使然&#xff0c;然而當三分鐘熱度過去之后&#xff0c;就要靠毅力支撐自己來完成它。至少我到現在是能非常深刻的體會這句話。一開始再怎么喜歡做一件事&#xff0c;要想…

mysql 導致iis 假死_解決IIS無響應假死狀態

1 查看服務器iis的w3wp.exe對應的應用程序池在IIS6下&#xff0c;經常出現w3wp的內存占用不能及時釋放&#xff0c;從而導致服務器響應速度很慢。今天研究了一下&#xff0c;可以做以下配置&#xff1a;1、在IIS中對每個網站進行單獨的應用程序池配置。即互相之間不影響。2、設…

Swift 5將強制執行內存獨占訪問

Swift 5將帶來改進的Swift程序內存安全性&#xff0c;在程序的其他部分修改變量時&#xff0c;不允許通過其他變量名來訪問這些變量。這個變更對現有應用程序的行為和Swift編譯器本身都有重要影響。Swift 5將帶來改進的Swift程序內存安全性&#xff0c;在程序的其他部分修改變量…

GitHub 支持上傳視頻文件啦!

大家好&#xff0c;我是若川。今天周六&#xff0c;分享一篇熱點新聞。文章較短&#xff0c;預計5分鐘可看完。近日 Github 宣布支持了視頻上傳功能&#xff0c;意味著&#xff0c;大家在提 issue 時可以攜帶視頻了&#xff0c;這極大地提高了開發者和維護者的效率&#xff0c;…

ui設計 網絡錯誤_UI設計人員常犯的10個錯誤

ui設計 網絡錯誤重點 (Top highlight)1.不考慮范圍 (1. Disregarding scope)It’s not uncommon for designers to introduce features that will overcomplicate the development process while bringing no additional value to the application. Focusing on the business o…

灰色的生命

也許幸福從來沒有在我身邊也許是已經在我身邊但我抓不住,摸不著 你的出現讓我驚奇為我灰色的生命添上了從沒見過的色彩我不相信這一切都是我的 不相信總是害怕相信了之后卻要面對殘酷的現實 但為何你讓我相信所有抓住了希望卻又轉過身沒有離去但讓一切停止望著你不想說…

小程序 node.js mysql_基于Node.js+MySQL開發的開源微信小程序B2C商城(頁面高仿網易嚴選)...

高仿網易嚴選的微信小程序商城(微信小程序客戶端)界面高仿網易嚴選商城(主要是2016年wap版)測試數據采集自網易嚴選商城功能和數據庫參考ecshop服務端api基于&#xff2e;ode.jsThinkJSMySQL計劃添加基于Vue.js的后臺管理系統、PC版、&#xff37;ap版項目截圖功能列表首頁分類…

前端菜鳥筆記 Day-5 CSS 高級

文章大綱來源&#xff1a;【Day 5】CSS 高級 CSS 選擇器CSS 拓展CSS 單位CSS 參考手冊CSS 選擇器 內容引用&#xff1a;CSS 選擇器 元素選擇器 html { ... } 復制代碼選擇器分組 h2, p { ... } 復制代碼類選擇器 .important { ... } p.warning { ... } .important.warning { .…

推薦幾個干貨超多助你成長的前端大佬

不得不說&#xff0c;如今比前些年學習資料多很多了。現在的前端公眾號也挺多的&#xff0c;這里推薦幾個前端大佬運營的公眾號&#xff0c;都是聚焦前端垂直領域的優質公眾號&#xff0c;關注這些公眾號至少可以&#xff1a;1、了解現在前端技術發展情況和未來發展趨勢&#x…

背景圖片_背景

背景圖片A designer’s journey is one that’s littered with many portfolios. Many of which have been reduced to a mere 404 error page, an abandoned link or another archive in the folders. Recently, while updating my portfolio, all the forgotten versions tha…

解決《Mobile繪制背景圖片》中的問題

與PC平臺的開發相比&#xff0c;Mobile的開發麻煩了許多&#xff0c;至少這是我的感覺 。 謝謝&#xff0d;&#xff0d;“ Fly Pig(^^)” 的文章《Mobile開發(繪制背景圖片) 》 http://www.cnblogs.com/Bright-Liang/archive/2009/06/11/1501309.html 不過對于我這種低手來說&…