class ts 扩展方法_一些让程序保持可扩展的 TypeScript 技巧
我們使用 TypeScript 的理由是,它有助于開發(fā)更快更安全的 app。
TypeScript 默認(rèn)會做很多簡化,這有助于開發(fā)者更容易的上手,但從長遠(yuǎn)來看,這些簡化也會浪費不少的開發(fā)時間。
我們收集了一系列更為嚴(yán)格的 TypeScript 編碼規(guī)則。只要你習(xí)慣一次,將來就會為你省下大量的編碼時間。
any
這是一條很簡單的規(guī)則,但長遠(yuǎn)看我們可以從中得到大大的好處。
永遠(yuǎn)都不要使用 “any”,永遠(yuǎn)!!!
原因很簡單,因為不會有任何場景需要使用 “any” 來描述一個類型。如果你遇到了使用 any 的情況,可能是架構(gòu)、代碼遺留或者其他特殊問題。
使用 泛型,未知類型 或者 重載 然后就不用擔(dān)心數(shù)據(jù)結(jié)構(gòu)上會出現(xiàn)意料之外的問題了。而這類問題的調(diào)試成本通常都很高。
嚴(yán)格模式
TypeScript 有 “嚴(yán)格” 模式,不過遺憾的這個模式默認(rèn)處于關(guān)閉狀態(tài)。此模式下有一系列的規(guī)則可以讓 TypeScript 用起來更安全舒適。如果不了解此模式,參考 這篇文章。
在 “嚴(yán)格” 模式下,你可以完全不用擔(dān)心諸如 undefined is not a function、cannot read property X of null 等錯誤。你的類型定義將會無比的準(zhǔn)確。
我得怎樣做?
如果你開了一個新項目,享受 “嚴(yán)格模式” 的樂趣吧。
如果你有一個非 “嚴(yán)格模式” 的項目,然后你還想為此項目開啟嚴(yán)格模式,那么你首先看到的就是一堆編譯問題。如果沒有編輯器的警告的話,編寫滿足嚴(yán)格模式的代碼是一件非常困難的事情,所以你在打開嚴(yán)格模式后很可能會碰到很多有問題的地方。因此遷移整個項目到 “嚴(yán)格模式” 很快就能讓人感到煩躁。
對此的建議是將這個大任務(wù)切割為小塊兒。“嚴(yán)格” 模式由 6 條規(guī)則組成。可以啟用其中的一條規(guī)則并且修復(fù)所有的錯誤。下一次啟用第二條規(guī)則,修復(fù)錯誤并且循環(huán)往復(fù)。終有一天會完全遷移到 “嚴(yán)格” 模式的。
tsconfig.json 文件
{
// ...,
"compilerOptions": {
// 一組很溜的規(guī)則配置
"noImplicitAny": true,
"noImplicitThis": true,
"strictNullChecks": true,
"strictPropertyInitialization": true,
"strictBindCallApply": true,
"strictFunctionTypes": true,
// 支持上述六個規(guī)則的快捷配置
"strict": true,
// ...
}
}
只讀
對我們 TypeScript 開發(fā)者來說,下一個重要的規(guī)則是無時無刻使用只讀。
在處理的過程中改變數(shù)據(jù)結(jié)構(gòu)是一種不好的實踐。舉個栗子,Angular 就不喜歡這種處理方式:當(dāng)轉(zhuǎn)換數(shù)據(jù)時,視圖的變更檢測和視圖更新會出現(xiàn)一些問題。
但你完全可以輕松的阻止所有的數(shù)據(jù)更改,只要養(yǎng)成寫 readonly 關(guān)鍵字的習(xí)慣。
我們應(yīng)該怎么做呢?
應(yīng)用中有很多地方可以將不安全的類型替換為只讀類型。
在 interface 中使用 “readonly” 屬性
// 之前
export interface Thing {
data: string;
}
// 之后
export interface Thing {
readonly data: string;
}
首選 “readonly” 類型
// 之前
export type UnsafeType = { prop: number };
// 之后
export type SafeType = Readonly; // Prefer “readonly” types
在類中盡可能的使用 “readonly” 字段
// 之前
class UnsafeComponent {
loaderShow$ = new BehaviorSubject(true);
}
// 之后
class SafeComponent {
readonly loaderShow$ = new BehaviorSubject(true);
}
使用 “readonly” 結(jié)構(gòu)
// 之前
const unsafeArray: Array = [1, 2, 3];
const unsafeArrayOtherWay: number[] = [1, 2, 3];
// 之后
const safeArray: ReadonlyArray = [1, 2, 3];
const safeArrayOtherWay: readonly number[] = [1, 2, 3];
// 三種寫法,三種安全等級
const unsafeArray: number[] = [1, 2, 3]; // 糟糕的
const safeArray: readonly number[] = [1, 2, 3]; // 還不錯
const verySafeTuple: readonly [number, number, number] = [1, 2, 3]; // 最棒的
// 映射:
// 之前
const unsafeMap: Map = new Map();
// 之后
const safeMap: ReadonlyMap = new Map();
// 集合:
// 之前
const unsafeSet: Set = new Set();
// 之后
const safeSet: ReadonlySet = new Set();
設(shè)為常量
在 TypeScript v.3.4 中我們可以使用 常量斷言 。
這是一個相比 “readonly” 更加嚴(yán)格的限制手段,因為它將實例封裝為最準(zhǔn)確的類型,你可以理解為沒有任何情況可以改變它的值。
設(shè)為常量 后還可以在 IDE 中獲取到準(zhǔn)確的類型提示。
實用類型
TypeScript 有一組特殊的類型,這些類型是其他類型的快捷轉(zhuǎn)換形式。
請查閱 實用類型的官方文檔 并且在 app 中使用他們。這將會為你節(jié)約很多時間。
縮小你的類型范圍
TypeScript 有很多工具可以縮小類型范圍。這個特性很有用,因為我們可以在項目中支持各種各樣場景下的嚴(yán)格限制的輸入。
看下如下樣例。這是一個很簡單的例子,不過它可以幫助我們理解類型檢查和類型范圍縮小的區(qū)別。
import {Subject} from 'rxjs';
import {filter} from 'rxjs/operators';
interface Data {
readonly greeting: string;
}
const data$$ = new Subject();
/**
* source$ 指定了類型 "Observable"
* 然而 "null" 并不能通過此函數(shù)過濾器
*
* 這是因為 typescript 不能確定類型是否產(chǎn)生了變化。
* 函數(shù) “value => !!value” 返回了布爾值但是未明確聲明類型
*/
const source$ = data$$.pipe(
filter(value => !!value)
)
/**
* 如下是一些比較好的例子
*
* 箭頭函數(shù)返回 “是否為 Data 類型的數(shù)據(jù)” 的結(jié)果
* 它縮小了類型范圍,并使 "wellTypedSource$" 擁有了正確的類型
*/
const wellTypedSource$ = data$$.pipe(
filter((value): value is Data => !!value)
)
// 如下代碼不能被編譯,你可以試下
// source$.subscribe(x => console.log(x.greeting));
wellTypedSource$.subscribe(x => console.log(x.greeting));
data$$.next({ greeting: 'Hi!' });
你可以通過如下方法縮小類型范圍:
**typeof** 來自 Javascript 的操作符,用于檢查原始類型
**instanceof** 來自 Javascript 的操作符,用于檢查被繼承的實例
**is T** TypeScript 的聲明,允許檢查復(fù)雜類型以及接口。不過你得小心的使用這個功能,因為確保類型正確是你的責(zé)任,而不是 TypeScript 的。
一些使用樣例:
// typeof 限制
function getCheckboxState(value: boolean | null): string {
if (typeof value === 'boolean') {
// value 只能是 "布爾" 類型
return value ? 'checked' : 'not checked';
}
/**
* 在此范圍內(nèi),值可能為 “null” 類型
*/
return 'indeterminate';
}
// instanceof 限制
abstract class AbstractButton {
click(): void { }
}
class Button extends AbstractButton {
click(): void { }
}
class IconButton extends AbstractButton {
icon = 'src/icon';
click(): void { }
}
function getButtonIcon(button: AbstractButton): string | null {
/**
* 在 "instanceof" 后 TS 就能知道數(shù)據(jù)類型是 "icon"
* field
*/
return button instanceof IconButton ? button.icon : null;
}
// is T 限制
interface User {
readonly id: string;
readonly name: string;
}
function isUser(candidate: unknown): candidate is User {
return (
typeof candidate === "object" &&
candidate !== null &&
"id" in candidate &&
"name" in candidate
);
}
const someData = { id: '42', name: 'John' };
if (isUser(someData)) {
/**
* TS 現(xiàn)在知道了 someData 實現(xiàn)了 User 接口
*/
console.log(someData.id, someData.name)
}
總結(jié)
特別是在安全且嚴(yán)格的 TS 開發(fā)過程中,這些技術(shù)在團隊中起著很重要的作用。當(dāng)然,它們也不能解決所有的問題,不過如果你一旦開始使用,這將使你能夠在大型項目中避開一些非預(yù)期的棘手的錯誤。
這篇文章是進(jìn)階 Angular - 大型 Angular 交互手冊的第二章。你可繼續(xù)瀏覽 Angular 相關(guān)的內(nèi)容:Angular 研究會
本文中的所有譯文僅用于學(xué)習(xí)和交流目的,轉(zhuǎn)載請務(wù)必注明文章譯者、出處、和本文鏈接
我們的翻譯工作遵照 CC 協(xié)議,如果我們的工作有侵犯到您的權(quán)益,請及時聯(lián)系我們。
總結(jié)
以上是生活随笔為你收集整理的class ts 扩展方法_一些让程序保持可扩展的 TypeScript 技巧的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: lbp特征提取算法 知乎_计算机视觉基础
- 下一篇: 多层陶瓷电容器用处_典型陶瓷电容的用途和