JavaScript >> Javascript 文檔 >  >> Tags >> TypeScript

將 Typescript 添加到現有的 Rails 應用程序

TypeScript 是由 Microsoft 開發和維護的 JavaScript 的強類型超集。強類型有助於您在開發過程中更早地編寫更清晰的代碼並檢測和修復潛在錯誤。

因為 TypeScript 是 JavaScript 的超集,所以任何現有的 JavaScript 程序也是有效的 TypeScript 程序。這意味著 TypeScript 可以與任何現有的 JavaScript 代碼無縫交互。這也意味著可以逐步完成從 JavaScript 到 TypeScript 的遷移。

儘管 TypeScript 和 JavaScript 可以很好地協同工作,但在規劃遷移時需要考慮許多重要因素。本文將為您提供堅實的基礎,以便您決定遷移是否適合您的項目。

將 TypeScript 添加到您的項目中

從 JavaScript 遷移到 TypeScript 時要記住的基本事項是,後者是寫在帶有 .ts 的文件中 擴展名而不是 .js .但是,您可以允許通過 TypeScript 編譯器處理 JavaScript 文件,以避免在 TypeScript 中重寫所有代碼。

在繼續之前,請確保您的文本編輯器或 IDE 已配置為使用 TypeScript。使用 TypeScript 的一個關鍵優勢是可以在編譯代碼之前在編輯器中報告錯誤以及智能代碼完成。 Visual Studio Code 內置了對 TypeScript 語言的支持,所以如果你使用這個編輯器,你不需要做任何事情。否則,應該很容易找到將 TypeScript 支持添加到您的編輯器的第三方插件。

設置好編輯器後,下一步就是將 TypeScript 編譯器添加到項目中。您可以通過 npm 執行此操作 :

$ npm install typescript --save-dev

上面的命令將 TypeScript 編譯器添加到您的項目中,可以使用 npx tsc 訪問 命令。您還可以全局安裝編譯器以使 tsc 命令可從任何地方訪問,但應首選本地安裝,以便構建可在不同機器上重現。

一旦你安裝了 TypeScript,你需要為你的項目創建一個配置文件。 TypeScript 使用 tsconfig.json 文件來管理項目的選項,例如要包含的文件和要執行的檢查類型。這是您可以開始的最小配置:

{
  "compilerOptions": {
    "target": "es5",
    "outDir": "dist"
    "allowJs": true,
  },
  "include": ["./src/**/*"]
}

在 JSON 文件中配置的最常見選項是 compilerOptionsinclude 特性。後者用於指定一組文件名或模式,以包含在程序中相對於 tsconfig.json 文件。它支持通配符來形成 glob 模式,它可能包含也可能不包含文件擴展名。如果省略文件擴展名(如上),則僅包含支持的擴展名:.ts , .tsx , .d.ts 默認情況下,使用 .js.jsx 如果 compilerOptions.allowJs 設置為 true .

compilerOptions 然而,屬性允許您確定編譯器在處理構建時應該有多鬆或多嚴格。這是您的大部分配置所在的位置。以下是上面指定的每個選項的作用:

  • target 上面的屬性允許您將較新的 JavaScript 語法轉換為較舊的版本,例如 ECMAScript 5。
  • allowJs 導致 TypeScript 編譯器接受 JavaScript 文件(包括導入)。這是一種通過允許 .ts 逐步轉換為 TypeScript 的方法 和 .tsx 文件與現有 JavaScript 文件並存。
  • outDir 導致構建輸出到 dist 文件夾。

此時,您可以使用 npx tsc --watch 在監視模式下運行編譯器 ,它會編譯源文件並將輸出發送到 dist 文件夾。

用 Webpack 編譯 TypeScript

有很多方法可以將 TypeScript 與現有的 Webpack 配置集成。如果使用 babel-loader 包轉譯 JavaScript 文件,可以添加 @babel/preset-typescript 預設生成 JavaScript 文件和 Fork TS Checker Webpack Plugin 包運行 TypeScript 類型檢查器,這樣如果有類型錯誤。

首先,使用 npm 安裝兩個包 :

$ npm install fork-ts-checker-webpack-plugin @babel/preset-typescript --save-dev

然後,更新您的 Webpack 配置文件以大致鏡像如下所示:

const path = require("path");
const ForkTsCheckerWebpackPlugin = require("fork-ts-checker-webpack-plugin");

const typescript = {
  test: /\.(ts|js)$/,
  use: [
    {
      loader: "babel-loader",
      options: {
        presets: ["@babel/preset-typescript"],
      },
    },
  ],
};

module.exports = {
  entry: {
    main: "./src/main.ts",
  },

  resolve: {
    extensions: [".ts", ".js"],
  },

  module: {
    rules: [typescript],
  },

  output: {
    path: path.resolve(__dirname, "dist"),
    filename: "[name].bundle.js",
  },

  plugins: [new ForkTsCheckerWebpackPlugin()],
};

此時,Webpack 將負責文件的轉譯和類型檢查,如果有任何錯誤將導致構建失敗。

將 TypeScript 添加到 Rails + Webpacker 項目

以下說明假設您已經有一個使用 Webpacker 5.1 或更高版本的 Rails 6 項目。您需要做的第一件事是使用以下命令為您的項目添加 TypeScript 支持:

$ bundle exec rails webpacker:install:typescript

這可以確保您的 TypeScript 代碼使用 Babel 進行轉譯(通過 @babel/preset-typescript 包裹)。如果您想在 Webpack 構建過程中啟用類型檢查,則需要手動安裝 Fork TS Checker Webpack 插件包。

$ yarn add --dev fork-ts-checker-webpack-plugin

然後,更新您的 config/webpack/development.js 文件如下圖:

const ForkTsCheckerWebpackPlugin = require("fork-ts-checker-webpack-plugin");
const path = require("path");

environment.plugins.append(
  "ForkTsCheckerWebpackPlugin",
  new ForkTsCheckerWebpackPlugin({
    typescript: {
      configFile: path.resolve(__dirname, "../../tsconfig.json"),
    },
    async: false,
  })
);

遷移到 TypeScript

將現有項目轉換為 TypeScript 基本上有兩種主要方法。第一種方法涉及用 TypeScript 重寫整個項目。這並不像聽起來那麼難,因為它只涉及將文件擴展名更改為 .ts.tsx 並修復編譯器發出的任何類型錯誤。這種方法的主要問題是您可能會遇到數百甚至數千個錯誤(取決於項目的大小和您的 TypeScript 配置的嚴格程度),並且您必須暫停新功能的開發直到您完成遷移,這可能會花費大量時間。

第二種更實用的方法是在代碼庫中支持混合使用 JavaScript 和 TypeScript 文件,並逐步將文件切換到 TypeScript。由於 TypeScript 編譯器的靈活性(通過 allowJs 選項),這個過程應該是輕而易舉的。您需要做的就是將 TypeScript 添加到構建過程並設置基本配置,例如本文前面介紹的配置。之後,您需要確保在 TypeScript 中實現任何新功能,同時以增量方式移動現有代碼。

一旦你通過 include 定義了你的依賴圖 或 files tsconfig.json 中的屬性 文件,編譯器將開始對代碼庫中的任何 TypeScript 文件進行類型檢查。您還可以通過 checkJs 啟用 JavaScript 文件的類型檢查 編譯器選項。這允許您使用 JSDoc 向 JavaScript 文件添加類型註釋,這樣您就可以開始在應用程序中使用類型,但無需完全提交到 TypeScript。

當您準備好提交時,您需要重命名 .js.jsx 文件到 .ts.tsx , 並開始使用 TypeScript 語法而不是 JSDoc 來定義類型。例如,假設您有一個 add.js 文件代碼如下:

// add.js
function add(x, y) {
  return x + y;
}

export default add;

此時,所有內容都隱式鍵入為 any .這意味著 TypeScript 不會對這些值執行任何類型檢查。您可以選擇使用 JSDoc 註釋對純 JavaScript 文件進行類型檢查,如下所示:

// add.js
/**
 * @param {number} x
 * @param {number} y
 * @returns {number}
 */
function add(x, y) {
  return x + y;
}

export default add;

如果 add,TypeScript 編譯器現在將報告錯誤 使用不正確,例如將字符串作為參數而不是數字傳遞時。

此時,您可能已準備好將文件移動到 TypeScript。只需將文件擴展名更改為 .ts 並將 JSDoc 註釋轉換為 TypeScript 語法。

// add.ts
function add(x: number, y: number): number {
  return x + y;
}

export default add;

有了這個策略,你就可以逐漸遷移到 TypeScript 而不會遇到太多摩擦。這種方法的主要警告是,由於缺乏這樣做的動力,大量代碼可能會保持無類型。

使用第三方庫

以上一節中描述的方式遷移到 TypeScript 非常適合您的應用程序代碼,但依賴一些可能需要一些額外配置以便保留使用 TypeScript 的好處的第三方庫的情況並不少見。

用 TypeScript 編寫的庫應該可以開箱即用,無需擺弄。使用兼容的編輯器時,您將能夠看到庫公開的所有內容,以及函數參數和返回值的類型。編譯器會確保你使用正確的類型,如果你不這樣做,編譯就會失敗。

但是,對於用 JavaScript 編寫的包(大多數),TypeScript 無法自動確定類型是什麼,因此它隱式分配了 any 鍵入整個庫。這是有問題的,因為 any 沒有任何類型安全性 type,所以即使你使用了一個不存在的方法,編譯器也不會報錯。

import * as lodash from "lodash";

// lodash.sneeze() is not a function
lodash.sneeze();

如果您打開了 noImplicitAny 編譯器標誌(推薦),則構建將失敗並出現類似於下面所示的錯誤。這實質上意味著 TypeScript 不知道哪些類型對 Lodash 庫有效。

從上面的錯誤消息中可以看出,有兩種主要方法可以解決此問題。先說一下聲明文件的方法。它涉及創建一個 .d.ts 描述另一個文件或包的類型定義的文件。例如,您可以創建一個 main.d.ts 源文件夾中的文件,內容如下:

declare module "lodash" {
  function sneeze(): string;
}

該文件指出 lodash 模塊公開了一個 sneeze 返回字符串的函數。如果再次運行構建,它會編譯,因為 TypeScript 相信聲明文件中的類型定義是正確的,並且它無法檢查它們是否真的準確。當然,代碼會拋出運行時錯誤,因為 sneeze 方法不存在。

如果您嘗試使用庫中的其他方法,則構建將再次失敗,直到將其類型定義添加到聲明文件中。這是向缺少類型的第三方庫添加類型的方法之一,使編譯器可以為代碼提供更強的保證。

將類型添加到純 JavaScript 包的第二種方法是通過 DefinedTyped 包,它是類型定義文件的社區來源存儲庫。如果您嘗試在項目中使用流行的 JavaScript 庫,則該庫的類型定義很可能已經貢獻到存儲庫中。這意味著您可以通過 npm 輕鬆地將它們帶入您的項目中 @types 下 範圍。例如,lodash 包的類型可以通過以下命令添加:

$ npm install --save @types/lodash

與任何其他 npm 包一樣,類型聲明包安裝在 node_modules 文件夾中。在其中,您會發現一個 @types 包含所有類型的文件夾。運行上面的命令後,你會發現一個lodash @types 內的文件夾 其中包含幾個文件,其中包含所有 lodash 方法的類型信息。 typescript 編譯器理解這個約定,因此它會自動識別類型而無需您的干預。

此時,可以去掉main.d.ts中的模塊定義 並再次構建項目。從上圖可以看出,它正確地報告了 sneeze 不存在。如果我們使用正確的方法,例如 ceil 使用正確的參數,它編譯得很好。此外,您還可以在編輯器中使用類型註釋獲得自動補全的好處。

請注意,DefinitelyTyped 打包中的類型定義是社區來源的,在大多數情況下不是由庫作者提供的。這意味著您可能會不時遇到丟失或不正確的定義。讓我們談談如果出現這種情況該怎麼辦。

合併聲明

TypeScript 編譯器允許將兩個或多個類型合併到一個定義中,前提是它們具有相同的名稱。這個合併的定義保留了兩個原始聲明的特徵。這裡有一個例子可以讓這個概念更容易理解。

interface Person {
  name: string;
}

interface Person {
  name: boolean;
  age: number;
}

const jack: Person = {
  name: "Jack",
  age: 20,
};

console.log(jack);

在這裡,兩個 Person 聲明被合併到一個定義中,所以 jack 對象包含來自兩個接口的所有屬性。這裡需要注意的一點是,後續的同名屬性聲明必須具有相同的類型。

interface Person {
  name: string;
}

interface Person {
  name: string; // works fine
  age: number;
}

interface Person {
  name: boolean; // throws an error
  age: number;
}

這本身可能看起來不是很有用,但如果你想擴展一個不是你定義的類型,它會派上用場。例如,假設 sneeze 方法確實存在於 lodash 但目前在 @types/lodash 中缺失 包裹。您可以通過 .d.ts 中的聲明合併來添加它 文件:

// main.d.ts
import * as _ from "lodash";

declare module "lodash" {
  interface LoDashStatic {
    sneeze(): string;
  }
}

要擴展一個模塊,您需要導入它並使用 declare module 進入模塊。如果您查看 @types/lodash 包,您會注意到所有方法都定義在 LoDashStatic 界面。要添加新方法,您只需在 lodash 中再次聲明接口 模塊並添加函數的類型定義。此時,如果您嘗試使用 sneeze 方法,它將與原始 LoDashStatic 上存在的所有其他方法一起編譯 界面。

這樣,您可以快速修復缺少的類型錯誤,而無需等待包更新。一旦相關@types 包更新發布,可以去掉.d.ts中的自定義定義 文件並通過 npm 更新包 ,其他一切都應該繼續工作。

結論

在同一個代碼庫中同時使用 JavaScript 和 TypeScript 可能需要一些時間來適應,但是類型定義和聲明合併的知識應該會讓事情變得容易得多。將整個代碼庫轉換為 TypeScript 後,就應該提高編譯器的嚴格性以提高安全性。此外,請查看 runtypes 和 io-ts 等庫,以對靜態類型進行運行時驗證。

感謝閱讀,祝您編碼愉快!


Tutorial JavaScript 教程
  1. 無法在表單中傳遞正確的值,做出反應

  2. 為什麼封閉的軟件比開放的好

  3. Ionic React Capacitor 應用程序中的 Google 登錄

  4. HTML 錨標記:完整指南。

  5. 是否可以在導出的同一文件中的類中使用導出的變量

  6. 如何實現按鈕禁用功能或其他技術,使用戶不會登錄兩次?反應,表達

  7. 如何在 JavaScript / RXJS 中將一系列日期綁定值映射/減少到運行總計?

  1. 你好!開發人員,請問我可以用 Dart 或 Flutter 知識替換 JavaScript 嗎?

  2. 快遞JS Hello World

  3. JavaScript 對象項順序

  4. 使用 jQuery 選擇 DOM 的初學者指南

  5. 6 種強大的 CSS 技術,你可以用它來代替 Javascript

  6. 為 Storybook 和 Web 應用程序構建暗模式

  7. 我構建了 Web 應用程序來搜索 GitHub 上的提交

  1. 使用 Phoenix LiveView 將文件上傳到 Google Drive

  2. 使用 React 和 Typesense 構建搜索應用程序

  3. Node.js 中的簡單內存緩存

  4. 將 Shopify 節點應用程序 Docker 映像部署到 GCP