TypeScript 的可區分聯合是一個強大的功能,可以將模式匹配提升到一個新的水平。它們使我們能夠創建複雜的、類型安全的條件邏輯,而不僅僅是簡單的 switch 語句。我在最近的專案中廣泛使用了這種技術,它改變了我在 TypeScript 中處理控制流程的方式。
讓我們從基礎開始。可區分聯合是一種使用公共屬性來區分不同變異的類型。這是一個簡單的例子:
type Shape = | { kind: 'circle'; radius: number } | { kind: 'rectangle'; width: number; height: number }
這裡的「種類」屬性是我們的判別式。它允許 TypeScript 根據其值推斷我們正在處理的特定形狀。
現在,讓我們看看如何使用它進行模式匹配:
function getArea(shape: Shape): number { switch (shape.kind) { case 'circle': return Math.PI * shape.radius ** 2 case 'rectangle': return shape.width * shape.height } }
這很簡潔,但這只是個開始。我們可以更進一步。
受歧視工會最強大的方面之一是詳盡檢查。 TypeScript 可以確保我們在模式匹配中處理了所有可能的情況。讓我們為我們的聯合添加一個新形狀:
type Shape = | { kind: 'circle'; radius: number } | { kind: 'rectangle'; width: number; height: number } | { kind: 'triangle'; base: number; height: number } function getArea(shape: Shape): number { switch (shape.kind) { case 'circle': return Math.PI * shape.radius ** 2 case 'rectangle': return shape.width * shape.height // TypeScript will now warn us that we're not handling the 'triangle' case } }
為了使其更加健壯,我們可以新增一個引發錯誤的預設情況,確保我們永遠不會意外忘記處理新情況:
function assertNever(x: never): never { throw new Error("Unexpected object: " + x); } function getArea(shape: Shape): number { switch (shape.kind) { case 'circle': return Math.PI * shape.radius ** 2 case 'rectangle': return shape.width * shape.height case 'triangle': return 0.5 * shape.base * shape.height default: return assertNever(shape) } }
現在,如果我們新增形狀而不更新 getArea 函數,TypeScript 會給我們一個編譯時錯誤。
但是我們可以透過模式匹配走得更遠。讓我們來看一個涉及嵌套模式的更複雜的範例。
想像一下我們正在為交通燈建造一個簡單的狀態機:
type TrafficLightState = | { state: 'green' } | { state: 'yellow' } | { state: 'red' } | { state: 'flashing', color: 'yellow' | 'red' } function getNextState(current: TrafficLightState): TrafficLightState { switch (current.state) { case 'green': return { state: 'yellow' } case 'yellow': return { state: 'red' } case 'red': return { state: 'green' } case 'flashing': return current.color === 'yellow' ? { state: 'red' } : { state: 'flashing', color: 'yellow' } } }
在這裡,我們不僅匹配頂級狀態,而且當我們處於「閃爍」狀態時也匹配嵌套屬性。
我們也可以使用守衛為我們的模式匹配添加更複雜的條件:
type WeatherEvent = | { kind: 'temperature', celsius: number } | { kind: 'wind', speed: number } | { kind: 'precipitation', amount: number } function describeWeather(event: WeatherEvent): string { switch (event.kind) { case 'temperature': if (event.celsius > 30) return "It's hot!" if (event.celsius < 0) return "It's freezing!" return "The temperature is moderate." case 'wind': if (event.speed > 100) return "There's a hurricane!" if (event.speed > 50) return "It's very windy." return "There's a gentle breeze." case 'precipitation': if (event.amount > 100) return "It's pouring!" if (event.amount > 0) return "It's raining." return "It's dry." } }
這種模式匹配方法不僅限於 switch 語句。我們可以將它與 if-else 鏈一起使用,甚至可以與物件文字一起使用以實現更複雜的場景:
type Action = | { type: 'INCREMENT' } | { type: 'DECREMENT' } | { type: 'RESET' } | { type: 'SET', payload: number } const reducer = (state: number, action: Action): number => ({ INCREMENT: () => state + 1, DECREMENT: () => state - 1, RESET: () => 0, SET: () => action.payload, }[action.type]())
這種方法在實作訪客模式時特別有用。這是我們如何使用可區分聯合來實作簡單表達式求值器的範例:
type Expr = | { kind: 'number'; value: number } | { kind: 'add'; left: Expr; right: Expr } | { kind: 'multiply'; left: Expr; right: Expr } const evaluate = (expr: Expr): number => { switch (expr.kind) { case 'number': return expr.value case 'add': return evaluate(expr.left) + evaluate(expr.right) case 'multiply': return evaluate(expr.left) * evaluate(expr.right) } } const expr: Expr = { kind: 'add', left: { kind: 'number', value: 5 }, right: { kind: 'multiply', left: { kind: 'number', value: 3 }, right: { kind: 'number', value: 7 } } } console.log(evaluate(expr)) // Outputs: 26
這種模式允許我們輕鬆地使用新類型的表達式來擴展我們的表達式系統,並且 TypeScript 將確保我們處理評估函數中的所有情況。
這種方法最強大的方面之一是它如何允許我們將大型、複雜的條件區塊重構為更易於管理和擴展的結構。讓我們來看一個更複雜的例子:
想像一下我們正在建立一個系統來處理不同類型的金融交易:
type Shape = | { kind: 'circle'; radius: number } | { kind: 'rectangle'; width: number; height: number }
在此範例中,我們使用 TypeScript 的映射類型和條件類型來建立類型安全對象,其中每個鍵對應於一種事務類型,每個值都是處理該特定類型事務的函數。這種方法使我們能夠輕鬆新增類型的交易,而無需更改handleTransaction函數的核心邏輯。
這種模式的美妙之處在於它既是類型安全的又是可擴展的。如果我們新增一個新的交易類型,TypeScript 會強制我們加入對應的處理器函數。如果我們嘗試處理不存在的交易類型,我們將收到編譯時錯誤。
這種具有可區分聯合的模式匹配方法可以產生更具表現力、更安全和自記錄的 TypeScript 程式碼,尤其是在複雜的應用程式中。它使我們能夠以可讀且可維護的方式處理複雜的邏輯。
隨著我們的應用程式變得越來越複雜,這些技術變得越來越有價值。它們使我們能夠編寫不僅正確而且易於理解和修改的程式碼。透過充分利用 TypeScript 的類型系統,我們可以創建健壯、靈活的系統,並且使用起來很愉快。
請記住,我們的目標不僅僅是編寫有效的程式碼,而是編寫能夠清楚表達其意圖並且能夠在需求變化時防止錯誤的程式碼。與可區分聯合的模式匹配是實現此目標的強大工具。
根據我的經驗,採用這些模式可以顯著提高程式碼品質和開發速度。需要一些時間來習慣用可區分的聯合和詳盡的模式匹配來思考,但一旦你這樣做了,你會發現它為以清晰、類型安全的方式構建代碼開闢了新的可能性。
當您繼續探索 TypeScript 時,我鼓勵您尋找機會在自己的程式碼中應用這些模式。從小事做起,也許可以將複雜的 if-else 鏈重構為可區分聯合。隨著您對這項技術越來越熟悉,您將開始看到越來越多的地方可以應用它來簡化和澄清您的程式碼。
請記住,TypeScript 的真正力量不僅在於它捕捉錯誤的能力,還在於它能夠引導我們獲得更好、更具表現力的程式碼結構。透過採用可區分聯合和詳盡模式匹配等模式,我們可以創建不僅正確,而且易於閱讀和維護的程式碼。
一定要看看我們的創作:
投資者中心 | 智能生活 | 時代與迴響 | 令人費解的謎團 | 印度教 | 精英開發 | JS學校
科技無尾熊洞察 | 時代與迴響世界 | 投資人中央媒體 | 令人費解的謎團 | | 令人費解的謎團 | >科學與時代媒介 |
現代印度教以上是掌握 TypeScript 的模式匹配:提高程式碼的功能和安全性的詳細內容。更多資訊請關注PHP中文網其他相關文章!