如何將三萬行代碼從 Flow 移植到 TypeScript?

2020-12-14 CSDN

【CSDN編者按】在內存安全中,類型安全是很重要的一個命題。為了確保JavaScript項目運行的類型安全,本文的作者介紹了2016年時使用Flow的經歷:由Facebook支持的Flow方案,不僅擁有查找類型、泛型參數默認值等基本功能,還有著較為完善的JavaScript開發生態系統。但是隨著項目的不斷複雜,以及TypeScript功能的逐漸優化,就對項目提出了更多的要求。本文就詳解了將三萬行代碼從Flow移植到TypeScript的全過程。

作者 | David Gomes譯者 | 彎月責編 | 郭芮出品 | CSDN

以下為譯文:

最近我們將MemSQL Studio的3萬行JavaScript代碼從Flow移植到了TypeScript。在本文中,我將介紹我們移植代碼庫的原因以及移植的全過程。

事先聲明,我寫這篇文章的目的不在於譴責Flow或Flow的使用。我非常欣賞Flow這個項目,我認為在JavaScript社區中Flow和TypeScript這兩種類型檢查器都有足夠的發展空間。但是,每個團隊都需要仔細研究並選擇最適合自己的。因此我真誠地希望這篇文章對你的選擇能有所幫助。

背景

首先介紹一下背景故事。在MemSQL,我們都喜歡靜態強類型的JavaScript代碼,這是為了避免動態弱類型的常見問題。例如:

動態弱類型問題中不同部分的代碼對於隱式類型契約的假設不一致,會引發運行時的類型錯誤;而且動態弱類型在測試小問題上花費的時間太多,比如參數類型檢查(運行時類型檢查也會增大打包文件的尺寸);此外動態弱類型還缺乏編輯器/ IDE集成,因為在沒有靜態類型的情況下,很難實現跳轉定義、自動重構以及其他功能;靜態強類型還具有動態弱類型問題所缺失的圍繞數據寫代碼的能力,這意味著我們可以先設計數據類型,然後我們的代碼就會「自然成型」。

這些還只是靜態類型部分的優點。

2016年初,我們開始在一個內部的JavaScript項目上使用tcomb,以確保運行時的類型安全(聲明:我並沒有參與那個項目)。雖然有時運行時的類型檢查很有用,但它與靜態類型毫不沾邊。考慮到這一點,2016年的時候我們決定在另一個項目中使用Flow。當時,Flow是一個很好的選擇,因為:

Flow由Facebook支持,在日益壯大的React社區中收到了相當多的好評(React也是用Flow開發的);我們沒有必要嘗試一個全新的JavaScript開發生態系統,拋棄Babel轉向tsc(TypeScript編譯器)的風險會很大,還會失去切換到Flow或其他類型檢查器的靈活性(顯然後來情況發生了變化);我們也沒有必要在整個代碼庫上採用類型(因為我們想先嘗試一下靜態類型的JavaScript),我們只想在部分文件上採用類型(不過請注意,現在的Flow和TypeScript都允許開發者這麼做);當時的TypeScript缺少Flow支持的一些基本功能,例如查找類型、泛型參數默認值等。

當2017年年末開始開發MemSQL Studio時,我們準備在整個應用程式中實現完整的類型覆蓋(整個應用程式都是用JavaScript編寫的,前端和後端都在瀏覽器中運行)。因為以前成功使用的經驗,所以我們決定此次也使用Flow。

然而,最新發布的Babel 7已經開始支持TypeScript了,這引起了我的注意。這個發布意味著採用TypeScript不再需要引入整個TypeScript生態系統,我們可以繼續通過Babel來生成JavaScript。更重要的是,這意味著實際上我們可以將TypeScript作為類型檢查器,而不是作為一種「語言」。

就個人而言,我認為將類型檢查與JavaScript的生成分離是在JavaScript中實現靜態(強)類型的更優雅的方式,因為:

將生成ES5和類型檢查從思想上進行某種程度的分離是一個好主意。如此一來可以減少類型檢查鎖定的範圍,並加快開發速度(即使類型檢查因某些原因而變慢,你的代碼生成也不會受到影響)。Babel擁有一些非常優秀的插件和了不起的功能,這些都是TypeScript的生成器所沒有的。例如,Babel允許開發者指定想要支持的瀏覽器,它將自動生成在這些瀏覽器上有效的代碼。不過這實現起來非常複雜,因此應當讓Babel做這一切,而不是讓社區在兩個不同項目上重複這種努力。我喜歡JavaScript這種程式語言(除了它缺少靜態類型),我不知道TypeScript會最終存活多久,但我相信ECMAScript會長期存在。出於這個原因,我更喜歡用JavaScript思考和寫代碼。

注意,我一直在說「使用Flow」或「使用TypeScript」,是因為我總是把它們當成工具,而非程式語言。

當然,這種方法也有一些缺點:

理論上,TypeScript編譯器可以根據類型執行優化,但如果將生成與類型檢查分離就失去這個優勢了;如果需要依賴很多工具和開發,那麼項目配置會變得稍複雜。不過我認為這個不足為慮,因為在我們的項目中Babel + Flow從來都沒出現過配置的問題。

TypeScript 能替代 Flow 方案嗎?

我注意到網上和本地JavaScript社區對TypeScript的興趣越來越濃厚。因此,當發現Babel 7支持TypeScript時,我就開始調查代替Flow的可能性。最重要的是,在使用Flow的時候我們遇到了很多挫折:

編輯器/ IDE集成的質量很低(與TypeScript相比)。Nuclide(Facebook自己的IDE,擁有最好的Flow集成)已經不再維護,所以沒什麼用了。社區很小。各種代碼庫數量較少,且總體的類型定義質量較低。Facebook和社區的Flow團隊之間缺乏公共的規劃,且互動很少。內存消耗很高且內存洩漏頻繁,我們團隊的工程師偶爾會經歷Flow佔用10GB的RAM的現象。

當然,我們還必須研究TypeScript是否合適我們。調查的過程非常複雜,但通過全面地閱讀文檔,我們發現Flow的每個功能在TypeScript中都有相應的支持。之後,我又研究了TypeScript的項目規劃,發現上面提到的功能都有非常滿意的支持(例如,我們在Flow中使用的一個部分類型參數推斷的功能)。

將三萬多行代碼從 Flow 移植到 TypeScript

實際上,將所有代碼從Flow移植到TypeScript的第一步是將Babel從6升級到7。這項工作看似簡單,但由於我們決定將Webpack 3升級到4,所以最後花了兩天的時間。由於我們的代碼中有一些遺留的依賴,所以此次的難度要比絕大多數JavaScript項目都高。

完成這一步後,我們就可以用新的TypeScript預設替換Babel的Flow預設,然後在用Flow編寫的完整原始碼上運行TypeScript編譯器——結果發生了8245個語法錯誤(只有在沒有語法錯誤的情況下tsc的命令行工具才會報告項目中的真正的錯誤)。

我們被這個數字嚇到了,但是很快我們就發現其中大部分是由於TypeScript不支持.js文件導致的。經過一番調查,我發現TypeScript文件必須以「.ts」或「.tsx」結尾(包含JSX的情況)。我不想在創建新文件的時候猶豫是應該使用「.ts」還是「.tsx」的擴展名,因為這是一種糟糕的開發體驗。所以,我決定將所有文件都重命名為「.tsx」(理想情況下,應當像Flow一樣所有的文件都具有「.js」擴展名,但我也可以接受使用「.ts」)。

經過這次修改後,我們有大約4000個語法錯誤。其中大多數都與導入類型有關,我們可以TypeScript的「import」替換,也可以使用Flow({||} vs {})中的密封對象表示法替換。在使用了幾個正則表達式替換之後,我們的語法錯誤數量降到了414個。剩下的部分只能手動修復了:

部分泛型類型參數推斷中使用的既存型別必須替換為顯式命名的各種類型的參數,或通過unknown類型告訴TypeScript我們並不關心某些類型的參數;$Keys類型和其他Flow高級類型在TypeScript中具有不同的語法,例如,$Shape <>與TypeScript中的Partial<>對應)。

修復了所有語法錯誤之後,tsc(TypeScript編譯器)終於告訴我們,代碼庫中大約有1300個真正的類型錯誤。這時我們不得不坐下商量是否還應該繼續,畢竟,如果要花費數周的開發時間,此次移植就得不償失了。但是,我們發現只需花費不到1周的時間就可以完成移植,所以我們決定繼續。

注意,在轉換期間,我們必須停止代碼庫的開發工作。當然,在移植期間依然可以繼續開發新代碼,但是你必須在可能有數百種之多的類型錯誤上進行工作,這不是一件易事。

都有哪些類型錯誤?

在很多方面,TypeScript和Flow都做出了不同的假設,在實踐中這意味著JavaScript代碼的行為會有所不同。在某些方面Flow更嚴格,而TypeScript在其他方面又更為嚴格。深入比較兩種類型檢查會花費大量時間,所以在本文中我只舉幾個例子。

注意:本文中所有的TypeScript練習環境(http://www.typescriptlang.org/play/)的連結都假設所有的「嚴格」設置都被打開了,但遺憾的是在分享TypeScript練習環境時,這些設置都不會保存到URL中。因此,可以點擊上面的連接打開TypeScript練習環境之後再手動設置。

invariant.js

我們的原始碼中有一個很常用的函數invariant,這個文檔(https://github.com/zertosh/invariant#invariantcondition-message)很好地解釋了它的功能:

var invariant = require('invariant');invariant(someTruthyVal, 'This will not throw');// No errorsinvariant(someFalseyVal, 'This will throw an error with this message');// Error raised: Invariant Violation: This will throw an error with this message

這是個非常簡單的函數,它能在某些條件下拋出異常。下面讓我們來看看在Flow中它的實現與使用:

type Maybe<T> = T | void;functioninvariant(condition: boolean, message: string) {if (!condition) {thrownewError(message); }}functionf(x: Maybe<number>, c: number) {if (c > 0) { invariant(x !== undefined, "When c is positive, x should never be undefined"); (x + 1); // works because x has been refined to "number" }}

下面,我們通過TypeScript運行完全相同的代碼片段。正如在連結中看到的那樣,TypeScript出錯了,因為它不清楚最後一行是否可以確保「x」不會被定義為undefined。這是一個眾所皆知的TypeScript的問題——它無法在函數中進行這類的推理。但是,由於這樣的代碼在我們代碼庫中很常見,所以我們就被迫手動替換每一個invariant的實例(有150多個):

type Maybe<T> = T | void;functionf(x: Maybe<number>, c: number) {if (c > 0) {if (x === undefined) {thrownewError("When c is positive, x should never be undefined"); } (x + 1); // works because x has been refined to "number" }}

雖然這不如invariant那麼好,但也不算大問題。

$ ExpectError vs @ ts-ignore

Flow有一個非常有趣的功能,類似於@ ts-ignore,不過不同的是如果下一行不是錯誤,那麼它就會出錯。在編寫「類型測試」時,這個功能很有用。類型測試可以確保類型檢查(無論是TypeScript還是Flow)按照我們的期望找到某些類型錯誤。

不幸的是,TypeScript沒有這個功能,這意味著我們的類型測試失去了部分價值——這也是我期待TypeScript能夠實現的功能。

一般的類型錯誤和類型推斷

通常,TypeScript會比Flow更清晰,如下例所示:

type Leaf = { host: string; port: number;type: "LEAF";};type Aggregator = { host: string; port: number;type: "AGGREGATOR";}type MemsqlNode = Leaf | Aggregator;functionf(leaves: Array<Leaf>, aggregators: Array<Aggregator>): Array<MemsqlNode> { // The next line errors because you cannot concat aggregators to leaves.return leaves.concat(aggregators);}

Flow推斷leaves.concat(aggregators) 的類型為Array<Leaf | Aggregator> ,然後將其轉換為Array<MemsqlNode>。我認為這是一個很好的例子,說明有的地方Flow很聰明,而TypeScript可能需要一點幫助(在這種情況下,我們可以用類型斷言來幫助TypeScript,但是類型斷言的使用很危險,請小心謹慎)。

儘管沒有正式的證據,但是我還是想說我認為在類型推斷方面Flow比TypeScript更優越。我非常希望TypeScript能夠向Flow看齊, 因為TypeScript正處於非常積極的開發中,並且最近TypeScript有了許多改進。而縱觀我們的原始碼,我們必須通過解釋或類型斷言給予TypeScript一些幫助(還是儘可能地避免使用類型斷言)。讓我們再來看一個例子(我們有200多個這種類型錯誤的實例):

type Player = {name: string; age: number; position: "STRIKER" | "GOALKEEPER",};type F = () =>Promise<Array<Player>>;const f1: F = () => {returnPromise.all([ {name: "David Gomes",age: 23,position: "GOALKEEPER", }, {name: "Cristiano Ronaldo",age: 33,position: "STRIKER", } ]);};

在TypeScript你不能這樣寫,因為它不允許你將{ name: "David Gomes", age: 23, type: "GOALKEEPER" }當作Player類型的對象(打開練習環境可以看到確切的錯誤)。這是另一個我覺得TypeScript「不夠聰明」的地方——至少與Flow相比不夠聰明。

為了修正這個錯誤,開發者有幾個選擇:

斷言"STRIKER"為"STRIKER",這樣TypeScript就可以理解該字符串是個有效的枚舉類型"STRIKER" | "GOALKEEPER";斷言整個對象為「Player」(as Player);或者(我認為的最佳解決方案)無需任何類型的斷言,只需寫Promise.all<Player>(...)。

另一個TypeScript的例子如下所示,這段代碼再次表明Flow具有更好的類型推斷:

type Connection = { id: number };declare functiongetConnection(): Connection;functionresolveConnection() {returnnewPromise(resolve => {return resolve(getConnection()); })}resolveConnection().then(conn => {// TypeScript errors in the next line because it does not understand// that conn is of type Connection. We have to manually annotate// resolveConnection as Promise<Connection>. (conn.id);});

一個很小但非常有趣的例子是Flow判斷Array<T>.pop()的類型為T,而TypeScript則認為它屬於T | void。這是我喜歡TypeScript的一個地方,因為它會強制你仔細檢查該項是否存在(如果數組為空,則Array.pop返回undefined)。

TypeScript對於第三方依賴的定義

當然,在編寫任何JavaScript應用程式時都有可能會有一些依賴。這些依賴都需要類型定義,否則開發者就失去了靜態類型分析的大部分威力(如本文開頭所述)。

從npm導入的庫可以附帶Flow類型定義或TypeScript類型定義,也可以兩者兼有或兩者都沒有。許多小型庫不帶有任何方式的類型,所以必須自己編寫類型定義,或從社區中找。Flow和TypeScript社區都有一個標準的JavaScript包的第三方類型定義代碼倉庫:flow-typed和DefinitelyTyped。

我不得不說使用DefinitelyTyped的體驗更好。在使用flow-typed的時候,我們必須通過它的命令行工具將各種依賴的類型定義引入到項目中。DefinitelyTyped找到了一個很好的方法與npm的命令行集成,即它的軟體包均以@types/package-name的方式命名。這一點非常了不起,有了它我們就可以很容易地為依賴引入類型定義了(jest、react、lodash、react-redux等等)。

除此之外,我花了大量時間向DefinitelyTyped貢獻代碼(當將代碼從Flow移植到TypeScript時,不要指望類型定義是等價的)。我已經發送了幾個拉取請求,所有工作都易如反掌。開發者只需要克隆、編輯類型定義、添加測試,然後發送拉取請求,DefinitelyTyped GitHub會將曾向這個類型定義貢獻過代碼的人標記為審核者。如果7日之內沒有人審核代碼,那麼DefinitelyTyped的維護者會審核PR。在合併到master分支後,新版本的依賴包將會發送到npm。例如,當我第一次更新@types/redux-form包時,在合併到master分支後版本7.4.14自動被推送到了npm。我們可以非常容易地更新package.json文件,就可以獲取新的類型定義。如果等不到PR被接受,那麼也可以隨時覆蓋項目中使用的類型定義。

總的來說,DefinitelyTyped中類型定義的質量更好,這要歸功於TypeScript背後的社區更大、更繁榮。事實上,在將我們的項目從Flow移植到TypeScript之後,我們的類型覆蓋率從88%提高到了96%,主要是由於更好的第三方依賴類型定義,「any」類型的依賴減少了。

Linting與測試

在移植過程中,我們發現使用TypeScript的eslint比較複雜,所以我們就選擇了tslint,從eslint轉移到了tslint。

此外,我們還使用ts-jest來運行TypeScript的測試。有些測試是有類型的,而有些是無類型的(如果給測試用例添加類型的工作量太大,我們就將它們保存成.js文件)。

修復了所有類型錯誤後,情況怎樣了?

經過歷時一周的修復工作後,我們遇到了最後一個類型錯誤,我們決定利用@ts-ignore將其暫且擱置。

在解決了一些代碼審查注釋並修復了一些錯誤之後(不幸的是,我們不得不修改少量運行時來修復TypeScript無法理解的邏輯),在這個PR被合併後,我們就開始使用TypeScript了。(還有,我們在後續的PR中修復了最後一個@ts-ignore)。

除了編輯器集成之外,TypeScript的使用體驗與Flow非常相似。Flow伺服器的性能稍微快一點,但這並不是一個大問題,因為在為你正在查看的文件提供內聯錯誤時它們一樣快。唯一的性能差異在於TypeScript需要更長的時間(約0.5到1秒)才能告訴你在保存某個文件後,項目中是否有新的錯誤。伺服器啟動時間大約相同(約2分鐘),但這並不重要。到目前為止,我們還沒遇到過內存消耗的問題,tsc使用的內存一直穩定在大約600Mb。

可能看起來Flow的類型推斷比TypeScript更好,但是兩個原因可以解釋為什麼這不是什麼大問題:

我們將代碼庫從Flow移植到了TypeScript,這意味著我們在其中發現了Flow可以表達但TypeScript卻不能表達的地方。如果這次移植是從TypeScript到Flow的,那麼我們可能就會發現TypeScript的推斷/表達比Flow更好。類型推斷很重要,它有助於保持我們的代碼更簡潔。但是強大的社區、類型定義的可用性等更為重要,因為弱類型推斷只需要加強下類型檢查就可以解決。

代碼統計

$ npm run type-coverage # https://github.com/plantain-00/type-coverage43330 / 45047 96.19%

MemSQL還有其他項目也打算棄用Flow、轉而投入TypeScript的懷抱(有些JavaScript項目可能一開始就使用TypeScript),所以我們希望使我們的TypeScript配置更加嚴格。

目前我們已經打開了「strictNullChecks」,但「noImplicitAny」仍處于禁用狀態,這也需要後續解決。

此外,我們還打算刪除代碼中的一些危險的類型斷言。

原文連結:https://davidgom.es/porting-30k-lines-of-code-from-flow-to-typescript/作者簡介:David Gomes,葡萄牙科英布拉大學畢業,軟體工程師@MemSQL。本文為 CSDN 翻譯,如需轉載,請註明來源出處。

相關焦點

  • 受夠了JavaScript的小毛病,我將整個應用移植到了TypeScript
    之後,我們分幾步將整個應用完全移植到了 TypeScript 上。本文介紹的是移植的第一步,也就是前端的部分。在 Execute Program 的原始 JavaScript 前端中,我經常會犯一些小錯誤。例如,我會將錯誤的 prop 名稱傳遞給 React 組件,或者遺漏某個 prop,抑或傳遞錯誤的數據類型。(Prop 是作為參數發送到 React 組件的數據。
  • 我為什麼要將Typescript與Express、nodejs一起使用(譯文)
    下面我們來看一下為什麼會發生這種情況:輸入safe = less errors通過在代碼中定義類型,您可以允許IDE在使用僅在運行時感知的類和函數時確認錯誤。例:這裡我使用的是Visual Studio Code,它指出了兩個錯誤:在第6行:我們試圖將字符串參數傳遞給只接受數字的函數。在第9行:我們試圖將一個返回數字的函數的結果賦給字符串。
  • 打造TypeScript的Visual Studio Code開發環境
    同時,VS Code的代碼提示、片段及調試功能也非常棒!本文將詳細闡述TypeScript與VS Code相結合的開發環境打造之道,為後續的學習提供先決條件。先總結一下TypeScript開發環境用到的各種工具:先按上述列表安裝Node與VS Code,接下來我們開始安裝其餘工具。
  • 從 React 遷移到 TypeScript:忍受了 15 年的 JavaScript 錯誤從此走遠
    對於像 JavaScript 和 Ruby 這樣的動態語言來說,這不是個小問題。過去 15 年來我一直在學習該如何應對這種錯誤問題。我在之前談論的是 2011 年代的情況,其中討論的緩解措施確實有些用途,但它們無法隨著系統的發展順利地擴展下去,而且我們忘掉它們時也沒有安全網可用。我覺得 15 年時間已經夠長了。
  • Tensorflow從入門到精通(二):附代碼實戰
    shape是指張量的形狀,如上述程序是生成一個2行3列的tensor;mean是指正態分布的均值;stddev是指正太分布的標準差;dtype是指生成tensor的數據類型;seed是分發創建的一個隨機種子;而name是給生成的隨機張量命名。
  • 最詳細從零開始配置 TypeScript 項目的教程
    TypeScript 如何自動生成庫包的聲明文件?TypeScript 目前是採用 TSLint 還是 ESLint 進行代碼校驗,為什麼?列舉你知道的所有構建工具並說說這些工具的優缺點?這些構建工具在不同的場景下應該如何選型?Babel 對於 TypeScript 的支持有哪些限制?如何確保構建和上傳的代碼無 ESLint 錯誤信息?ESLint 和 Prettier 的區別是什麼?
  • 最詳細的從零開始配置 TypeScript 項目的教程
    TypeScript 如何自動生成庫包的聲明文件?TypeScript 目前是採用 TSLint 還是 ESLint 進行代碼校驗,為什麼?列舉你知道的所有構建工具並說說這些工具的優缺點?這些構建工具在不同的場景下應該如何選型?Babel 對於 TypeScript 的支持有哪些限制?如何確保構建和上傳的代碼無 ESLint 錯誤信息?ESLint 和 Prettier 的區別是什麼?
  • vue高級進階系列——用typescript玩轉vue和vuex
    接下來,我不會過多介紹vuex的用法,而是介紹如何基於typescript,用class的方式來使用vue和vuex進行項目開發,相信使用過react的朋友們對class的寫法不會陌生,那就讓我們開始吧!為了省去一些配置上的麻煩,我們直接採用vue-cli3來搭建項目。在創建項目的時候選中typescript即可。
  • 或許是網上最詳細從零開始配置 TypeScript 項目的教程
    TypeScript 如何自動生成庫包的聲明文件?TypeScript 目前是採用 TSLint 還是 ESLint 進行代碼校驗,為什麼?列舉你知道的所有構建工具並說說這些工具的優缺點?這些構建工具在不同的場景下應該如何選型?Babel 對於 TypeScript 的支持有哪些限制?如何確保構建和上傳的代碼無 ESLint 錯誤信息?ESLint 和 Prettier 的區別是什麼?
  • 【超詳細教程】如何使用TypeScript和GraphQL開發應用
    在本教程中,我將首先解釋為什麼要結合這些技術,然後通過使用TypeGraphQL從頭構建API來展示如何將TypeScript與GraphQL結合使用。先決條件本教程假定您有使用TypeScript的經驗,尤其是對TypeScript類和裝飾器。GraphQL的知識將派上用場,但不是強制性的。
  • 使用pix2pix-tensorflow 的交互式圖象到圖象翻譯的演示
    原文地址:連結最近,我將Isola等人做的pix2pix移植到了Tensorflow 平臺。Tensorflow 平臺包含在Tensorflow的圖像到圖像翻譯(Image-to-ImageTranslation in Tensorflow)論文中。我採用了一些預訓練的模型,並製作了一個網絡互動的程序可以直接嘗試玩玩。
  • 從 JavaScript 到 TypeScript - 聲明類型
    要為變量或者常量指定類型也很簡單,就是在變量/常量名後面加個冒號,再指定類型即可,比如// # typescript// 聲明函數 pow 是 number 類型,即返回值是 number 類型// 聲明參數 n 是 number 類型function pow(n: number): number {    return
  • 移植Lua到ARM平臺
    因此Lua的移植灰常簡單,只需要使用正確的交叉編譯器就好了。本文介紹如何將Lua移植到ARM平臺,分x個步驟,並使用如下環境:本文引用地址:http://www.eepw.com.cn/article/201611/317673.htm1. 作業系統:Ubunt10.042.
  • 如何從Tensorflow中創建CNN,並在GPU上運行該模型(附代碼)
    在本教程中,您將學習卷積神經網絡(CNN)的架構,如何在Tensorflow中創建CNN,並為圖像標籤提供預測。
  • TypeScript 3.0 正式發布:引入「項目引用」新概念
    官方表示,儘管版本號是一個大變化,但 3.0 版本幾乎沒有破壞性的變更(這意味著我們可以很輕鬆地升級到該版本),3.0 引入了一種新的靈活且可擴展的方式來構建項目、提供對參數列表進行操作新的強大支持能力、強制顯式檢查的新類型、更好的 JSX 支持、整體更好的錯誤處理,等等。下面簡單說說值得關注的更新,更詳細的更新說明請移步至 發布說明。
  • 模型秒變API只需一行代碼,支持TensorFlow等框架
    這個工具能讓你一行代碼直接打包。專注於機器學習應用的人們知道,從訓練好的模型到實際的工業生產工具還有一定的距離。其中工作量很大的地方在於將模型打包,預留 API 接口,並和現有的生產系統相結合。近日,GitHub 上有了這樣一個項目,能夠讓用戶一行代碼將任意模型打包為 API。這一工具無疑能夠幫助開發者在實際的生產應用中快速部署模型。
  • MIT 深度學習基礎教程:七個基本框架TensorFlow代碼實戰
    專知以前介紹的MIT深度學習課程:作為麻省理工學院深度學習系列講座和GitHub教程的一部分,我們將介紹使用神經網絡解決計算機視覺、自然語言處理、遊戲、自動駕駛、機器人等領域問題的基礎知識。我們如何表現世界,可以讓複雜的東西在我們人類和我們建立的機器學習模型看來都很簡單。 作者最喜歡的之前的一個例子是1543年哥白尼發表的日心說,它將太陽置於「宇宙」的中心,而不是以地球為中心的地心模型。
  • CANopen原始碼移植
    本教程使用的是CANopen 原始碼,基於stm32 F407介紹CANopen原始碼SO-877的整體架構和在移植過程中的一些經驗和注意事項。可以實現CANopen從站和簡單CANopen主站功能。當程序運行是,出現錯誤時,CANopen 層會調用一些標準函數,通過串口輸出錯誤的一些說明,錯誤代碼,方便調試錯誤,比如錯誤代碼0x0013,查看錯誤代碼表,發現是CAN初始化錯誤,這時候就需要檢查CAN的初始化,經檢查確認是CAN的Rx和Tx在程序移植時對應的引腳不同。這樣減少了移植差錯時間。比如0x0078,這時候就要檢查PDO映射和對象字典了。
  • 實現一個 async/await (typescript 版)
    關於 async/await 的原理的文章,網上也有很多了,但是本文既然是使用 typescript 來寫,我們的 async/await 也是要能夠通過用戶傳入的函數自動推斷出結果,所以如何對其編寫 typescript 定義也是本文的一個重要板塊。
  • MLflow:一種機器學習生命周期管理平臺
    在這篇博文中,我將簡要概述MLflow所應對的挑戰,以及如何入門的初階講解。機器學習工作流的挑戰在Databricks,我們與數百家在生產環境中使用機器學習的公司合作。 在這些公司中,我們一再聽到圍繞機器學習的相同的擔憂擔憂:存在無數彼此獨立的工具。 從數據準備到模型訓練,數百種軟體工具涵蓋了機器學習生命周期的每個階段。