## 前情提要
在寫程式產生器的過程中,很常遇到會一直重複使用的方法,例:字串轉換、陣列群組...。
一般來說我會直接在專案下建立 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)