原文連接:TypeScript, why is so important?html
譯文連接:TypeScript 爲什麼如此重要?前端
衆所周知的經典編程語言,例如:Pascal、C、C++等都是強類型語言,這就意味着這些語言,必須在編譯時設置更嚴格的類型規則。node
每次你聲明變量或函數參數時,必須先明確標明它們的類型,而後再使用。這個概念背後的緣由能夠追溯到好久之前,即所謂的爲了確保程序有意義的類型理論。es6
硬件沒法區分類型。類型能夠看做是人抽象出來的東西,可讓編程者實現更高層次的思考,讓代碼更加簡潔明瞭。typescript
此外,從編譯器的角度來看,類型還有一些優點,例如:便於優化。在編譯階段進行類型檢查可讓編譯器更有效率的執行機器指令。安全是另外一個重要的考量,強類型系統能夠幫助編譯器提前發現錯誤。npm
隨着像是 Basic,JavaScript,PHP,Python 等解釋型語言新秀的出現,它們都是在運行時進行類型檢查。編程者能夠不用編譯它們的代碼,語言變得更靈活智能,能夠基於上下文和數據進行類型檢測。編程
你們不該該就關於強類型和弱類型孰優孰劣展開一場新爭論,咱們必須瞭解,每一種語言都是基於某個特定的目的被設計創造出來的,沒有人會預料到像是 JavaScript 這樣的腳本語言會如此流行並普遍的應用於開發商業應用。json
給像 JavaScript 這樣的弱類型語言增長強類型的能力,不只能夠幫助開發團隊寫出整潔的自解釋代碼,並且能解決一個根本問題:在運行時以前的編譯階段捕獲類型錯誤。數組
JavaScript 是一個解釋型或者說動態編譯語言,開發人員在運行程序以前不須要編譯代碼。由於,咱們稱 TypeScript 爲JavaScript 的類型超集,意思是說它給開發人員提供了一組新的語法,能夠給 JavaScript 這種弱類型語言加入類型。瀏覽器
舉個例子,當咱們在 JavaScript 中聲明一個變量時,是不須要指定類型的。但在 TypeScript 中聲明變量就必須指定一個類型,固然你也能夠不設置類型直接賦值。
let isDone: boolean let decimal: number let big: bigint let color: string let name = "John"
跟 JavaScript(.js)不一樣,TypeScript 文件後綴使用 .ts 擴展名。瀏覽器是不識別 .ts 文件,因此使用時必須提早把 TS 代碼轉換成 JavaScript 代碼。這個轉換過程被稱爲轉譯,編譯和轉譯的微小差異在於:
實話實說,我必須澄清這個概念,由於我已經有不少次碰到這兩個容易被混淆的概念了。不過,爲了便於閱讀,就連 TypeScript 的官方文檔也一直把預處理過程叫作編譯。
咱們可使用 npm
和 yarn
安裝 TypeScript
yarn add typescript
或
npm install typescript
而後,咱們就可使用 tsc
命令編譯 TS 文件。
npx tsc
在咱們的項目中新建 TS 文件,並而後在命令行中用 tsc
編譯。咱們新建一個文件叫作 app.ts
。
function add(num1: number, num2: number): number { return num1 + num2 }
而後再命令行執行:
npx tsc app.ts
會生成一個以下內容的名字叫作 app.js 的文件。
function add(num1, num2) { return num1 + num2 }
不過,有更簡單的方式。最簡單的一種是在你 JS 根目錄建立一個 tsconfig.json 的文件,讓編譯器經過此配置執行。
{ "compilerOptions": { "target": "es6", "rootDir": "./src", "outDir": "./dist", "module": "commonjs", "removeComments": true }, "include": ["src/**/*"], "exclude": ["node_modules", "**/*.spec.ts"] }
此配置文件按照部分劃分,以下咱們能看到一個最基本的配置文件有如下選項:
給 TypeScript 定義新的配置文件後,咱們就能在 src 文件夾下新建多個 TypeScript 文件,而後,咱們只須要在命令行運行 npx tsc
,就能編譯文件而且把生成的文件放到輸出文件夾。
咱們也能夠在 package.json
中指定一個 tsc
任務,甚至可使用 watch
選項讓文件在被改動後自動運行 tsc
。
根據你使用的技術和項目類型,能夠有多種方式設置 TypeScript ,在本文中,咱們不會展現全部可能的配置方案,你們若是想了解更多的選項,鼓勵你們去看一下官方文檔。
TypeScript 是一個在軟件開發過程當中幫助開發者給數據類型添加更嚴格的約束的工具。他必須跟其餘好的實踐一塊兒配合例如適當使用 let
或 const
替換 var
進行局部變量聲明。
讓咱們來回顧一下 TS 提供的類型。
基本類型的聲明以下:
let isDone: boolean = false let decimal: number = 6 let hex: number = 0xf00d let binary: number = 0b1010 let octal: number = 0o744 let big: bigint = 100n let color: string = 'blue'
數組類型有兩種寫法:
let list: number[] = [1, 2, 3]
或者
let list: Array<number> = [1, 2, 3]
好比咱們要建立第一個元素是 string
和 第二個元素是 number
的數組這樣或者相似的場景,咱們就可使用 Tuple
。
let x: [string, number] x = ['hello', 10]
重要的是要理解 TS 對類型及其聲明的順序施加了嚴格的控制,因此,基於上面的定義,下面的代碼就會報錯。
x = [10, 'hello'] // WRONG
與其餘語言(例如:C 或 C++)同樣,TypeScript 也具備用於聲明多個常數的枚舉類型。跟其餘語言不一樣的是,TS 的枚舉更靈活。
enum Color { Red, Green, Blue } let c: Color = Color.Green
枚舉從 0 開始,因此 Red = 0 , Green = 1 , Blue = 2 ,不過在 TS 中,你能夠經過下面的方式改變順序:
enum Color { Red = 1, Green, Blue }
或者給每一個常量分配不一樣的數字
enum Color { Red = 2, Green = 6, Blue = 5 }
甚至能夠給每一個常量分配字符串類型的值
enum Color { Up = "Up", Down = "Down", Left = "Left", Right = "Right" }
如今,咱們已經知道了如何定義基本類型,可是,在弱類型語言中添增強類型校驗會在不少方面產生巨大影響。
例如,咱們正在跟 DOM 進行交互,想從一個 HTML 元素中獲取 value。咱們能夠指明元素的類型,可是要確保,從元素上獲取 value 以前,必須確保它存在。
const elem = document.getElementById('elementId')! as HTMLInputElement
最後的感嘆號是告訴 TS,雖然 TS 不能肯定元素上是否存在這個值,可是咱們能夠接受這個風險。
另外一個有趣的例子是,當咱們須要指明函數接受到的參數多是字符串或者數字時,換句話說,咱們傳參能夠是字符串或數字。
對於這個場景,咱們可使用管道符(|)合併全部可能接收的類型:
function combine(a: number | string, b: number | string): void { //logic to validate types and perform operations }
這個管道還能夠用來指明做爲參數的特殊的字符串
function foo(color: 'yellow' | 'brown'){...}
上面的例子中,函數接收的字符串參數必須是 yello 或 brown 之一。
函數的返回類型也是須要重點關注的,若是咱們想建立一個拋出錯誤的函數,它的返回值是什麼類型的?
就像這個例子, TS 有一種類型叫作:never。這個類型是指不會發生。不過,它常常被用做函數拋出異常。
function error(msg: string): never { throw new Error('msg') }
另外,函數沒有返回應該用 void
聲明。
function message(msg: string): void { console.log('msg') }
若是不知道數據是什麼類型的,咱們可使用 unknown
關鍵字。在下面的例子中,TypeScript 不會控制它的類型,不過,必須在分配給其餘類型前進行類型驗證。
let input: unknown /before assigning it we should check its type if(typeof input === "string") { let name: string = input }
除了在賦值以前進行類型檢查外,咱們還能夠給它的類型轉換爲咱們知道的類型。在 TypeScript 的強制轉換以下:
let myinput: unknown let mylength: number = (<string>myinput).length
或者
let myinput: unknown let mylength: number = (myinput as string).length
有些狀況咱們不想讓 TS 進行類型檢查,好比,當咱們使用一個不能控制的外部的庫,或者咱們須要定義一個有可能返回任意類型的函數。對於這些狀況,咱們可使用 any
。
declare function getValue(key: string): any const str: string = getValue('test')
跟其餘語言相似,接口與定義類型相關,建立接口類型的對象時,必須遵照接口類型的定義。
因此,咱們假設一個函數接收一個 user 對象。在使用它以前咱們能夠先建立一個接口來約束對象的結構或者說是規則。
interface User { name: string age: number } function displayPersonalInfo(user: User) { console.log(`Name: ${user.name} - Age: ${user.age}`) }
建立接口時,能夠添加一些修飾符,相似 ?
代表屬性多是 null
,也可使用 readonly
關鍵字設置一個不可修改的屬性。
interface Square { color?: string width?: number } interface Point { readonly x: number readonly y: number } let square: Square = { width: 14, }
順便說一下,readonly
是一個有趣的關鍵字,能夠應用於其餘類型。例如,存在一個 ReadonlyArray
定義,可讓開發者建立一個元素不能修改的數組。
let a: number[] = [1, 2, 3, 4] let ronumbers: ReadonlyArray<number> = a ronumbers[0] = 4 //WRONG! It cannot be assigned //But it could be used for iterating over its values for reading purposes for (const num of ronumbers) { console.log(num) }
最後要重點介紹面嚮對象語言最關鍵的特性之一:泛型,在 TypeScript 中也是存在的。
可複用組件是每一種現代強類型語言的基礎,引入了強類型語言的 JavaScript 也是如此,咱們必須給開發者一種能夠定義對於不一樣的類型數據有相同處理邏輯的函數。
對於使用過像是 C++,C#,Kotlin,Java 甚至 Rust 的人來講,他們對這個概念很是熟悉。
對於其餘開發人員,咱們仍是要解釋一下,泛型是一種聲明數組、類或函數的方法,數組,類或函數在聲明過程當中使用了他們不知道的類型。
泛型的用法是一對 <>
,中間能夠包含任何字母,這些字符在以後的實現邏輯中做爲標記,並在定義發生時被實際類型替換。
function myMax<T>(x: T, y: T): T { return x > y ? x : y } const intMax = myMax<number>(12, 50) console.log(intMax)
上面的例子中,咱們定義了一個比較兩個值並返回最大的那一個的函數,注意,實際類型(number)是在後面才傳入的。
咱們能夠總結一下 TypeScript ,做爲一個靜態類型的校驗語言,給 JavaScript 這個前端語言增長了一層邏輯讓它更健壯。仔細觀察,咱們還能夠了解到大多數語言是如何添加相似的特性的:函數式編程、lambda 函數,強類型,不可變的變量等。
這是一個好現象,由於它代表了軟件行業日趨成熟,而且對於入行開發的新人及後來人來講會更好。