TypeScript:從JavaScript到TypeScript的平滑過渡,就像升級你的樂高積木建造過程!
「盡其所能,用其所有」——這是我奉行的座右銘之一。這句話也反映了成長型思維模式的一部分。我們大多數前端或JavaScript開發者,都已經開始或完全遷移到TypeScript了。但有些人可能仍難以理解其概念,或難以將思考方式從JavaScript轉換到TypeScript的模式。為了解決這個問題,我們將使用我最喜歡的工具之一:樂高積木。讓我們從這裡開始:「將JavaScript想像成一套基礎樂高積木,你可以自由搭建;而TypeScript則是同一套積木,但配有詳細的說明書和品質控制檢查。」關於TypeScript的更深入探討,可以參考這裡、這裡以及這段影片。本指南旨在展示每個JavaScript概念如何轉換為TypeScript,並使用樂高類比來幫助你更容易理解這些概念。
變數作用域是指程式中變數可存取和使用的上下文。主要有兩種作用域:局部作用域和全域作用域。在任何函數外部聲明的變數位於全域作用域,這意味著可以在程式碼的任何地方存取和修改它。另一方面,在函數內部宣告的變數位於局部作用域,並且只能在該函數內存取。 JavaScript使用var
、let
和const
關鍵字來聲明變量,每個關鍵字對作用域的影響都不同。用let
和const
宣告的變數是區塊作用域的,這意味著它們只能在最近的封閉區塊{}
內存取。相較之下,var
是函數作用域的,使其在其聲明的整個函數中可用。清楚地理解變數作用域有助於避免變數名稱衝突和JavaScript程式中意外的副作用等問題。
提升是指在程式碼執行(編譯階段)之前,變數和函數宣告被移到其包含作用域頂部的行為。這意味著可以在聲明變數和函數之前使用它們。函數宣告是完全提升的,允許即使在程式碼中定義之前也能呼叫它們。但是,使用var
聲明的變數會被提升,但不會初始化其初始值,因此在賦值之前訪問它們將導致undefined
。用let
和const
聲明的變數也會被提升,但不會被初始化,如果在聲明之前訪問它們會導致ReferenceError
。理解提升有助於開發人員透過正確建構變數和函數宣告來避免常見的陷阱。
將作用域想像成不同的樂高搭建房間:
<code class="language-javascript">// 全局搭建房间 const globalBricks = "每个人都可以使用这些"; function buildSection() { // 个人搭建桌 var tableBricks = "仅供此搭建者使用"; if (true) { // 特定区域 let sectionBricks = "仅供此部分使用"; } }</code>
<code class="language-typescript">// 为我们的搭建房间添加类型安全 type BrickType = "regular" | "special" | "rare"; const globalBricks: BrickType = "regular"; function buildSection(): void { // TypeScript确保我们只使用有效的积木类型 const tableBricks: BrickType = "special"; if (true) { // TypeScript阻止在此块之外使用sectionBricks let sectionBricks: BrickType = "rare"; } } // 真实世界的例子:配置管理 interface AppConfig { readonly apiKey: string; environment: "dev" | "prod"; features: Set<string>; } const config: AppConfig = { apiKey: "secret", environment: "dev", features: new Set(["feature1", "feature2"]) };</code>
函數是旨在執行特定任務的可重複使用程式碼區塊。這增強了模組化和程式碼效率。它們可以使用function
關鍵字定義,後面跟著名稱、括號()
和用大括號{}
括起來的程式碼區塊。參數可以在括號或大括號內傳遞到函數中,這些參數充當呼叫函數時提供的值的佔位符。 JavaScript也支援匿名函數(沒有名稱)和箭頭函數(提供更簡潔的語法)。函數可以使用return
語句傳回值,或是執行不傳回值的運算。此外,JavaScript中的函數是一等對象,這意味著它們可以賦值給變數、作為參數傳遞以及從其他函數返回,從而實現函數式程式設計模式。
閉包是一個強大的特性,它允許函數記住並存取其詞法作用域,即使函數在該作用域之外執行也是如此。當在一個函數內部定義一個函數並引用外部函數中的變數時,可以建立閉包。即使外部函數執行完畢後,內部函數仍然可以存取這些變數。此功能對於資料封裝和在事件處理程序或回調等環境中維護狀態非常有用。閉包支援私有變數等模式,其中函數可以公開特定行為,同時隱藏實作細節。
<code class="language-javascript">function buildHouse(floors, color) { const foundation = "concrete"; return function addRoof(roofStyle) { return `${color} house with ${floors} floors and ${roofStyle} roof on ${foundation}`; }; }</code>
<code class="language-typescript">// 带有类型的基本函数 interface House { floors: number; color: string; roofStyle: string; foundation: string; } // 为我们的搭建者添加类型安全 function buildHouse( floors: number, color: string ): (roofStyle: string) => House { const foundation = "concrete"; return (roofStyle: string): House => ({ floors, color, roofStyle, foundation }); } // 真实世界的例子:组件工厂 interface ComponentProps { id: string; style?: React.CSSProperties; children?: React.ReactNode; } function createComponent<T extends ComponentProps>( baseProps: T ): (additionalProps: Partial<T>) => React.FC<T> { return (additionalProps) => { // 组件实现 return (props) => <div></div>; }; }</code>
JavaScript中的物件是基本的資料結構,用作相關資料和功能的容器。它們由鍵值對組成,其中每個鍵(屬性)都對應到一個值,該值可以是任何有效的JavaScript類型,包括函數(方法)。物件可以透過幾種方式建立:
const obj = {}
new Object()
Object.create()
方法原型系統是JavaScript內建的繼承機制。每個物件都與另一個物件(稱為其原型)具有內部連結。當嘗試存取物件上不存在的屬性時,JavaScript會自動在其原型鏈中尋找它。此對象鏈會持續到到達具有null
原型的對象,通常是Object.prototype
。理解原型對於以下方面至關重要:
將物件和原型想像成這樣:
<code class="language-javascript">// 全局搭建房间 const globalBricks = "每个人都可以使用这些"; function buildSection() { // 个人搭建桌 var tableBricks = "仅供此搭建者使用"; if (true) { // 特定区域 let sectionBricks = "仅供此部分使用"; } }</code>
<code class="language-typescript">// 为我们的搭建房间添加类型安全 type BrickType = "regular" | "special" | "rare"; const globalBricks: BrickType = "regular"; function buildSection(): void { // TypeScript确保我们只使用有效的积木类型 const tableBricks: BrickType = "special"; if (true) { // TypeScript阻止在此块之外使用sectionBricks let sectionBricks: BrickType = "rare"; } } // 真实世界的例子:配置管理 interface AppConfig { readonly apiKey: string; environment: "dev" | "prod"; features: Set<string>; } const config: AppConfig = { apiKey: "secret", environment: "dev", features: new Set(["feature1", "feature2"]) };</code>
非同步函數是JavaScript中一種特殊的函數類型,它提供了一種優雅的方式來處理非同步操作。當用async
關鍵字宣告時,這些函數會自動傳回一個Promise,並在其主體中啟用await
關鍵字的使用。 await
運算子會暫停函數的執行,直到Promise被解決或拒絕,從而允許以更同步、更易讀的風格編寫非同步程式碼。此語法有效地減少了回調的複雜性,並消除了對嵌套Promise鏈的需求。例如,在async function fetchData() { const response = await fetch(url); }
中,函數會在繼續執行之前等待fetch
操作完成,使程式碼的行為更可預測,同時確保主執行緒保持不被阻塞。當處理相互依賴的多個非同步操作時,此模式特別有用,因為它允許開發人員編寫清楚地表達操作順序的程式碼,而不會犧牲效能。
Promise表示一個可能現在可用、未來可用或永遠無法使用的值。它是一個有三種可能狀態的物件:等待中、已完成或已拒絕。它用於處理非同步操作。 Promise具有.then()
、.catch()
和.finally()
等方法,用於根據結果連結操作。這使得它們成為嵌套回調的有力替代方案,提高了程式碼的可讀性和錯誤處理能力。
<code class="language-javascript">// 全局搭建房间 const globalBricks = "每个人都可以使用这些"; function buildSection() { // 个人搭建桌 var tableBricks = "仅供此搭建者使用"; if (true) { // 特定区域 let sectionBricks = "仅供此部分使用"; } }</code>
<code class="language-typescript">// 为我们的搭建房间添加类型安全 type BrickType = "regular" | "special" | "rare"; const globalBricks: BrickType = "regular"; function buildSection(): void { // TypeScript确保我们只使用有效的积木类型 const tableBricks: BrickType = "special"; if (true) { // TypeScript阻止在此块之外使用sectionBricks let sectionBricks: BrickType = "rare"; } } // 真实世界的例子:配置管理 interface AppConfig { readonly apiKey: string; environment: "dev" | "prod"; features: Set<string>; } const config: AppConfig = { apiKey: "secret", environment: "dev", features: new Set(["feature1", "feature2"]) };</code>
這是一種將數組中的值或物件中的屬性提取到不同的變數中的簡潔方法。數組解構使用方括號[]
,而物件解構則使用大括號{}
。此語法透過將值直接解包到變數中,減少了對重複程式碼的需求,從而更容易處理複雜的資料結構。例如,const [a, b] = [1, 2]
將1賦值給a,將2賦值給b,而const { name } = person
從person物件中提取name屬性。
展開運算子由三個點(...)表示。它允許將諸如數組或物件之類的可迭代物件擴展到需要多個元素或鍵值對的地方。它可以用於複製、組合或將陣列元素作為函數參數傳遞。例如,const arr = [1, 2, ...anotherArray]
。
可選鏈由?.
表示。它提供了一種安全的方式來存取深度嵌套的物件屬性,而不會在屬性未定義或為null時導致錯誤。如果引用為nullish,它會短路並立即傳回undefined。例如,user?.address?.street
在訪問street之前檢查user和address是否存在。此語法可防止執行階段錯誤,並使處理巢狀資料結構更加簡潔且不易出錯,尤其是在API或依賴使用者輸入的資料中。
<code class="language-javascript">function buildHouse(floors, color) { const foundation = "concrete"; return function addRoof(roofStyle) { return `${color} house with ${floors} floors and ${roofStyle} roof on ${foundation}`; }; }</code>
<code class="language-typescript">// 带有类型的基本函数 interface House { floors: number; color: string; roofStyle: string; foundation: string; } // 为我们的搭建者添加类型安全 function buildHouse( floors: number, color: string ): (roofStyle: string) => House { const foundation = "concrete"; return (roofStyle: string): House => ({ floors, color, roofStyle, foundation }); } // 真实世界的例子:组件工厂 interface ComponentProps { id: string; style?: React.CSSProperties; children?: React.ReactNode; } function createComponent<T extends ComponentProps>( baseProps: T ): (additionalProps: Partial<T>) => React.FC<T> { return (additionalProps) => { // 组件实现 return (props) => <div></div>; }; }</code>
從JavaScript到TypeScript的過渡就像升級你的樂高搭建過程:
JavaScript(基礎搭建):
TypeScript(專業搭建):
關鍵過渡技巧:
記住:TypeScript是在你的JavaScript知識的基礎上建構的,它增加了安全性和清晰度,而不是改變基本的搭建過程。也就是說,我的建議還是…先學習JavaScript,然後再學習TypeScript。
以上是使用 TypeScript 建置:基於樂高的指南的詳細內容。更多資訊請關注PHP中文網其他相關文章!