## 前情提要 在寫程式產生器的過程中,很常遇到會一直重複使用的方法,例:字串轉換、陣列群組...。 一般來說我會直接在專案下建立 common 資料夾,並在此資料夾內建立對應的方法程式檔案,並在各處會使用到該方法的地方引入該方法。 ==TS== **src/lib/common/string.ts** ```typescript= /**首字大寫並使用駝峯取代分隔字 * @param val 字串 * @param separator 分隔字 */ export function getAUpperCamel(val: string, separator = '-') { const isRegExp = separator.startsWith('/') && separator.endsWith('/'); separator = isRegExp ? new RegExp(separator.substring(1, separator.length - 1)) as any : separator; const ary = val.split(separator); let camel = ''; ary.forEach((itm, index) => { const aStr = itm.substring(0, 1).toUpperCase(); const zStr = itm.substring(1).toLowerCase(); camel += `${aStr}${zStr}`; }) return camel; } ``` ==TS== **src/lib/component/test.component.ts** ```typescript= import { getAUpperCamel } from '../common/string'; str = getAUpperCamel('hello-world'); //output:HelloWorld ``` 但跟著跟發的程式檔案越多,我開始想是不是有機會擴充原本型別的 prototype ,讓專案內的對應型別皆提供我寫好的方法,這樣就不需要在每個程式內都去 import 方法了。 ## 實作 型別的擴充可以透過 `.d.ts` 檔案讓編譯器讀懂程式的型別,其中設定的地方在 `tsconfig.json` 裡,如果不是在自己的專案內也可以在 `include` 設定擴充(第三方,例:`../common3/src/**/*.d.ts`)的型別檔案。 ==JSON== **tsconfig.spec.json** ```jsonld= /* To learn more about this file see: https://angular.io/config/tsconfig. */ { "extends": "../../tsconfig.json", "compilerOptions": { "outDir": "../../out-tsc/spec", "types": [ "jasmine" ] }, "include": [ "**/*.spec.ts", "**/*.d.ts", "../common3/src/**/*.d.ts" ] } ``` ### Step1: 建立 `types/**.d.ts` ==TS== **src/types/global.d.ts** ```typescript= export { } declare global { interface String { toAUpperCamel(separator?: string): string; } } ``` :::info 如果專案是比較複雜的,寫完後型別判斷不正確,記得回頭檢查 `tsconfig.json` 的設定是否需要手動加入自訂的型別檔。 例: 在專案 Common3 底下有一個 APP 與多個 LIB,LIB 在開發時想要引入在 APP 定義的 type,這個時候可能就需要手動將 commom3 定義的 type 在 `tsconfig` 中設定。 ==檔案結構示意== - project: common3 - application > common3 - library > dream-dev ==JSON== dream-dev/tsconfig.spec.json ```json= { ... "include": [ "**/*.spec.ts", "**/*.d.ts", "../common3/src/**/*.d.ts" ] ... } ``` ::: ### Step2: 建立 `utils/**.ts` 接著就是自定義的屬性實作了,我們可以透過 `String.protytype.** = {function}` 來實現方法擴充。 ==TS== **stringExtensions.ts** ```typescript= import { getALowerCamel } from '../common/string'; String.prototype.toAUpperCamel = function (separator: string) { return getAUpperCamel(String(this), separator) }; ``` ### Step3: 在入口檔案引入 完成所有擴充方法後,在入口引入就可以確保所有的 `String.prototype` 都會被加載擴充的方法,通常會推薦在 `main.ts` 或 `polyfills.ts` 中加上 `import './utils/stringExtensions';`。 ==TS== **main.ts** ```typescript= import { bootstrapApplication } from '@angular/platform-browser'; import { appConfig } from './app/app.config'; import { AppComponent } from './app/app.component'; import './utils/stringExtensions'; bootstrapApplication(AppComponent, appConfig) .catch((err) => console.error(err)); ``` 做完上面的步驟,可以使用`String` 的擴充功能了! ## 補充文章 [Declaration Files: Introduction](https://www.typescriptlang.org/docs/handbook/declaration-files/introduction.html) [Declaration Files: Declaration Reference](https://www.typescriptlang.org/docs/handbook/declaration-files/by-example.html) [[TypeScript] 用生活例子圖解 Utility Types](https://medium.com/hannah-lin/typescript-%E7%94%A8%E7%94%9F%E6%B4%BB%E4%BE%8B%E5%AD%90%E5%9C%96%E8%A7%A3-utility-type-2eeee27a58cd)