深入解析TypeScripe幾個特點 - Alex

banq發表於2021-03-04

TypeScript是一種功能強大的靜態型別化語言。很多時候,它被稱為“ JavaScript的超集”。但是,對於某些功能,它會強制以特定方式編寫程式碼。
 

類魔法
TypeScript對class關鍵字有特殊的支援。對於(模組的)全域性範圍內的每個類,它隱式定義一個具有相同名稱的例項型別。這樣可以編寫類似const user: User = new User()。
不幸的是,該機制不適用於動態建立的類或普通建構函式。在這種情況下,必須使用實用程式InstanceType和關鍵字typeof。

//正常類
class StaticClass {}
const a: StaticClass /* 型別例項 */ = new StaticClass(); /* 構造器 */

//下面是
動態建立的類

const createClass = () => class {};
const DynamicClass = createClass(); /* 無隱性型別定義 */
// 現在這種寫法無效: const b: DynamicClass = new DynamicClass();

type DynamicClass = InstanceType<typeof DynamicClass>; /* 現在有了型別 */
const b: DynamicClass /* 型別例項 */ = new DynamicClass(); /* 構造器*/

export {StaticClass, DynamicClass}; /* 都輸出構造器和型別 */


語句type X = InstanceType<typeof X>在邏輯上等效於TypeScript在遇到class關鍵字時自動執行的操作。
 

沒有成員的型別推斷
對於介面的某些實現,可以推斷成員屬性和成員函式的型別。例如,當介面Logger定義函式log(message: string): void時,其實現型別ConsoleLogger只可以使用方法簽名log(message)。TypeScript可以推斷出function引數是一個字串,返回值是void。由於不同的原因,目前不支援此功能。必須明確地顯式型別化所有成員屬性和成員函式,而與介面或基類無關。
下一個示例說明了由於這種情況導致的潛在重複:

interface Logger {
  logInfo(message: String): void;
  logWarning(message: String): void;
  logError(message: String): void;
}

class ConsoleLogger implements Logger {
  logInfo(message: String) { /* .. */ }
  logWarning(message: String) { /* .. */ }
  logError(message: String) { /* .. */ }
}

 

沒有部分型別推斷
TypeScript可以根據其用法來推斷型別引數的型別。例如,

asArray<T>(item: T) { return [item]; }

可以在不指定型別引數(例如)的情況下呼叫該函式asArray('foo')。在這種情況下,T被推斷為型別"foo"(extends string)。但是,這不適用於多個型別的引數,只能推斷其中的一些。一種可能的解決方法是將一個函式拆分為多個,其中一個具有要推斷的所有型別引數。
以下程式碼顯示了一個通用函式,用於使用預填充的資料建立物件工廠:

const createFactory1 = <R extends {}, P extends {}>(prefilled: P) =>
  (required: R) => ({...required, ...prefilled});
// requires to specify second type parameter, even though it could be inferred
const createAdmin1 = createFactory1<{email: string}, {admin: true}>({admin: true});
const adminUser1 = createAdmin1({email: 'john@example.com'});

const createFactory2 = <R extends {}>() => <P extends {}>(prefilled: P) =>
  (required: R) => ({...required, ...prefilled});
// first function specifies type parameter, for second function it is inferred
const createAdmin2 = createFactory2<{email: string}>()({admin: true});
const adminUser2 = createAdmin2({email: 'jane@example.com'});


函式createFactory1()需要指定兩個型別引數,即使可以推斷出第二個引數。createFactory2()透過將該功能分為兩個單獨的操作,消除了此問題。
 

區分聯合Discriminating Unions用法
區分聯合對於處理類似專案的異類集(例如“領域事件”)很有用。該機制允許使用區分欄位來區分多種型別。每種專案型別都為該欄位使用一種特定的型別,以使其與眾不同。處理具有聯合型別的專案時,可以根據區分欄位來縮小其型別。這種機制的一個缺點是,它要求以特定的方式編寫程式碼。
下一個示例將事件處理程式的JavaScript實現與其具有Discriminate Unions的TypeScript比較:

// JavaScript
const handleEvent = ({type, data}) => { // early destructuring
  if (type == 'UserRegistered')
    console.log(`new user with username: ${data.username}`);
  if (type == 'UserLoggedIn')
    console.log(`user logged in from device: ${data.device}`);
};

// TypeScript
type UserRegisteredEvent = {type: 'UserRegistered', data: {username: string}};
type UserLoggedInEvent = {type: 'UserLoggedIn', data: {device: string}};
type UserEvent = UserRegisteredEvent | UserLoggedInEvent;

const handleEvent = (event: UserEvent) => { // destructuring must not happen here
  if (event.type == 'UserRegistered')
    console.log(`new user with username: ${event.data.username}`);
  if (event.type == 'UserLoggedIn')
    console.log(`user logged in from device: ${event.data.device}`);
};

使用TypeScript時,在下溯其型別之前,請勿將具有Discriminate Union型別的值進行分解。
 

模板文字Template Literal型別
模板文字型別本質上是型別級別上的模板文字。它們可用於建立字串文字型別,這些型別是評估模板文字的結果。David Timms的文章“在TypeScript 4.1中探索模板文字型別”透過高階示例對它們進行了更詳細的說明。一種值得注意的用例是訊息處理元件的定義,其中各個訊息型別由特定操作處理。
以下示例使用先前的記錄器示例對此進行了演示:

type MessageType = 'Info' | 'Warning' | 'Error';

type Logger = {
  [k in MessageType as `log${MessageType}`]: (message: string) => void;
}

class ConsoleLogger implements Logger {
  logInfo(message: String) { /* .. */ }
  logWarning(message: String) { /* .. */ }
  logError(message: String) { /* .. */ }
}


型別定義Logger在聯合型別MessageType上進行迭代,併為每種MessageType定義一個操作。
 

總結
儘管TypeScript的好處可能勝過其潛在的弊端,但要意識到這些弊端仍然很重要:首先,區分聯盟會影響使用解構分配的方式。同樣,缺少部分型別推斷可能需要將一個功能拆分為多個功能。
 

相關文章