根據對 90,000 名開發人員的 Stack Overflow 調查,TypeScript 是人們最想學習的框架之一。前端
在過去幾年中,TypeScript 的受歡迎程度,社區規模和採用率都在不斷提升。 目前,Facebook 的 Facebook Jest 項目正在向 TypeScript 轉移。typescript
TypeScript 是 JavaScript 的靜態類型超集,提供了類型系統和對 ES6 的支持,它能夠編譯成純 JavaScript。編譯出來的 JavaScript 能夠運行在任何瀏覽器上。TypeScript 編譯工具能夠運行在任何服務器和任何系統上編程
JavaScript 在過去幾年中發展了不少。 它是用於客戶端和服務器端的最通用的跨平臺語言。數組
但 JavaScript 從未意味着進行如此大規模的應用程序開發。 它是一種沒有類型系統的動態語言,這意味着變量能夠具備任何類型的值,例如字符串或布爾值。瀏覽器
類型系統可提升代碼質量,可讀性,並使代碼庫的維護和重構更容易。 更重要的是,錯誤能夠在編譯時而不是在運行時捕獲。安全
若是沒有類型系統,很難擴展 JavaScript 以構建複雜的應用程序,而大型團隊則使用相同的代碼。服務器
TypeScript 在編譯時提供代碼不一樣部分之間的保證。 編譯器錯誤一般會告訴您確切的錯誤位置以及出現了什麼問題,而運行時錯誤伴隨着堆棧跟蹤可能會產生誤導並致使花費大量時間在調試工做上。前端工程師
const isLoading: boolean = false
複製代碼
const decimal: number = 8
const binary: number = 0b110
複製代碼
const fruit: string = 'orange'
複製代碼
數組類型能夠用如下兩種方式之一編寫:框架
// 常見的
let firstFivePrimes: number[] = [2, 3, 5, 7, 11]
// 不常見。 使用泛型類型(稍後會詳細介紹)
let firstFivePrimes2: Array<number> = [2, 3, 5, 7, 11]
複製代碼
數組合並了相同類型的對象,而元組(Tuple)合併了不一樣類型的對象。元組起源於函數編程語言(如 F#),在這些語言中頻繁使用元組。dom
let contact: [string, number] = ['John', 954683]
contact = ['Ana', 842903, 'extra argument'] /* Error! Type '[string, number, string]' is not assignable to type '[string, number]'. */
複製代碼
any 與類型系統中的任何和全部類型兼容,這意味着能夠將任何內容分配給它,而且能夠將其分配給任何類型。它使您可以選擇不經過類型檢查。
let variable: any = 'a string';
variable = 5;
variable = false;
variable.someRandomMethod(); /_ Okay,
someRandomMethod might exist at runtime. _/
複製代碼
void 是沒有任何類型的。它一般用做不返回值的函數的返回類型。
function sayMyName(name: string): void {
console.log(name)
}
sayMyName('Heisenberg')
複製代碼
never 類型表示從未發生的值的類型。例如,never 函數的返回類型將始終拋出異常或未達到其終點。
// throws an exception
function error(message: string): never {
throw new Error(message)
}
// unreachable end point
function continuousProcess(): never {
while (true) {
// ...
}
}
複製代碼
可使用 null 和 undefined 來定義這兩個原始數據類型,它們自己並非很是有用,可是當它們在聯合類型中使用時會變得很是有用 (等下會有更多內容)
type someProp = string | null | undefined
複製代碼
TypeScript 3.0 引入了未知類型,它是任何類型安全的對應類型。 任何東西均可以分配給未知的,可是未知的東西除了自己和任何東西外都不能分配。 若是沒有先聲明或縮小爲更具體的類型,則不容許對未知操做進行操做。
type I1 = unknown & null // null
type I2 = unknown & string // string
type U1 = unknown | null // unknown
type U2 = unknown | string // unknown
複製代碼
類型別名提供類型註釋的名稱,容許您在多個位置使用它。它們使用如下語法建立:
type Login = string
複製代碼
TypeScript 容許咱們爲屬性使用多種數據類型。這稱爲聯合類型。
type Password = string | number
複製代碼
混合類型是組合全部成員類型的屬性的類型
interface Person {
name: string
age: number
}
interface Worker {
companyId: string
}
type Employee = Person & Worker
const bestOfTheMonth: Employee = {
name: 'Peter',
age: 39,
companyId: '123456'
}
複製代碼
在 TypeScript 中,咱們使用接口(Interfaces)來定義對象的類型。 在面嚮對象語言中,接口(Interfaces)是一個很重要的概念,它是對行爲的抽象,而具體如何行動須要由類(classes)去實現(implement)。 TypeScript 中的接口是一個很是靈活的概念,除了可用於對類的一部分行爲進行抽象之外,也經常使用於對「對象的形狀(Shape)」進行描述。 附註:接口的運行時 JS 影響爲零,它僅用於類型檢查。
您能夠聲明標記帶有?的可選屬性,這意味着接口的對象可能定義這些屬性,也可能不定義這些屬性。 您能夠聲明只讀屬性,這意味着一旦爲屬性分配了值,就沒法更改它。
interface ICircle {
readonly id: string
center: {
x: number
y: number
}
radius: number
color?: string // 可選屬性
}
const circle1: ICircle = {
id: '001',
center: {
x: 0
},
radius: 8
}
/* Error! Property 'y' is missing in type '{ x: number; }' but required in type '{ x: number; y: number; }'. */
const circle2: ICircle = {
id: '002',
center: {
x: 0,
y: 0
},
radius: 8
} // Okay
circle2.color = '#666' // Okay
circle2.id = '003'
/* Error! Cannot assign to 'id' because it is a read-only property. */
複製代碼
接口能夠擴展一個或多個接口。這使得編寫接口變得靈活且可重用。
interface ICircleWithArea extends ICircle {
getArea: () => number
}
const circle3: ICircleWithArea = {
id: '003',
center: { x: 0, y: 0 },
radius: 6,
color: '#fff',
getArea: function() {
return this.radius ** 2 * Math.PI
}
}
複製代碼
實現接口的類須要嚴格遵循接口的結構。
interface IClock {
currentTime: Date
setTime(d: Date): void
}
class Clock implements IClock {
currentTime: Date = new Date()
setTime(d: Date) {
this.currentTime = d
}
constructor(h: number, m: number) {}
}
複製代碼
一個 enum(或枚舉)是組織相關的值,能夠是數值或字符串值的集合的方式。
enum CardSuit {
Clubs,
Diamonds,
Hearts,
Spades
}
let card = CardSuit.Clubs
card = 'not a card suit' /* Error! Type '"not a card suit"' is not assignable to type 'CardSuit'. */
複製代碼
Under the hood(不會翻譯), 枚舉默認狀況下是基於數字的。enum 值從零開始,每一個成員遞增 1。
咱們以前的示例生成的 JavaScript 代碼:
var CardSuit
;(function(CardSuit) {
CardSuit[(CardSuit['Clubs'] = 0)] = 'Clubs'
CardSuit[(CardSuit['Diamonds'] = 1)] = 'Diamonds'
CardSuit[(CardSuit['Hearts'] = 2)] = 'Hearts'
CardSuit[(CardSuit['Spades'] = 3)] = 'Spades'
})(CardSuit || (CardSuit = {}))
/** * 這致使如下對象: * { * 0: "Clubs", * 1: "Diamonds", * 2: "Hearts", * 3: "Spades", * Clubs: 0, * Diamonds: 1, * Hearts: 2, * Spades: 3 * } */
複製代碼
或者,可使用字符串值初始化枚舉,這是一種更易讀的方法。
enum SocialMedia {
Facebook = 'FACEBOOK',
Twitter = 'TWITTER',
Instagram = 'INSTAGRAM',
LinkedIn = 'LINKEDIN'
}
複製代碼
enum 支持反向映射,這意味着咱們能夠從其值中訪問成員的值以及成員名稱。 映射到咱們的 CardSuit 示例:
const clubsAsNumber: number = CardSuit.Clubs // 3
const clubsAsString: string = CardSuit[0] // 'Clubs'
複製代碼
您能夠爲每一個參數添加類型,而後添加到函數自己以添加返回類型。
function add(x: number, y: number): number {
return x + y
}
複製代碼
TypeScript 容許您聲明函數重載。基本上,您可使用相同名稱但不一樣參數類型和返回類型的多個函數。請考慮如下示例:
function padding(a: number, b?: number, c?: number, d?: any) {
if (b === undefined && c === undefined && d === undefined) {
b = c = d = a
} else if (c === undefined && d === undefined) {
c = a
d = b
}
return {
top: a,
right: b,
bottom: c,
left: d
}
}
複製代碼
每一個參數的含義根據傳遞給函數的參數數量而變化。並且,該函數只須要一個,兩個或四個參數。要建立函數重載,只需屢次聲明函數頭。最後一個函數頭是一個其實是活躍中的函數體,但不是提供給外面的世界。
function padding(all: number) function padding(topAndBottom: number, leftAndRight: number) function padding(top: number, right: number, bottom: number, left: number) function padding(a: number, b?: number, c?: number, d?: number) { if (b === undefined && c === undefined && d === undefined) { b = c = d = a } else if (c === undefined && d === undefined) { c = a d = b } return {
top: a,
right: b,
bottom: c,
left: d
}
}
padding(1) // Okay
padding(1, 1) // Okay
padding(1, 1, 1, 1) // Okay
padding(1, 1, 1) /* Error! No overload expects 3 arguments, but overloads do exist that expect either 2 or 4 arguments. */
複製代碼
您能夠向屬性和方法的參數添加類型
class Greeter {
greeting: string
constructor(message: string) {
this.greeting = message
}
greet(name: string) {
return `Hi ${name}, ${this.greeting}`
}
}
複製代碼
TypeScript 可使用三種訪問修飾符(Access Modifiers),分別是 public、private 和 protected。 public 修飾的屬性或方法是公有的,能夠在任何地方被訪問到,默認全部的屬性和方法都是 public 的 private 修飾的屬性或方法是私有的,不能在聲明它的類的外部訪問 protected 修飾的屬性或方法是受保護的,它和 private 相似,區別是它在子類中也是容許被訪問的
Accessible on | public | protected | private |
---|---|---|---|
class | yes | yes | yes |
class children | yes | yes | no |
class instance | yes | no | no |
A readonly property must be initialised at their declaration or in the constructor.
class Spider {
readonly name: string
readonly numberOfLegs: number = 8
constructor(theName: string) {
this.name = theName
}
}
複製代碼
參數屬性容許您在一個位置建立和初始化成員。它們經過在構造函數參數前加上修飾符來聲明。
class Spider {
readonly numberOfLegs: number = 8
constructor(readonly name: string) {}
}
複製代碼
abstract 關鍵字既可用於類,也可用於抽象類方法。
TypeScript 容許您以任何方式覆蓋其推斷類型。當您比變換器自己更好地理解變量類型時,可使用此方法。
const friend = {}
friend.name = 'John' // Error! Property 'name' does not exist on type '{}'
interface Person {
name: string
age: number
}
const person = {} as Person
person.name = 'John' // Okay
複製代碼
最初類型斷言的語法是<type>
let person = <Person>{}
複製代碼
可是這在 JSX 中使用時產生了歧義。所以建議改成使用 as。
類型斷言一般在從 JavaScript 遷移代碼時使用,您可能知道變量的類型比當前分配的更準確。但斷言可被視爲有害。
咱們來看看上一個示例中的 Person 接口。你注意到了什麼問題嗎?若是您注意到失蹤的房產年齡,恭喜!編譯器可能會幫助您爲 Person 的屬性提供自動完成功能,但若是您錯過任何屬性,它將不會抱怨。
當沒有類型註釋形式的可用顯式信息時,TypeScript 會推斷變量類型。
/** * 變量定義 */
let a = 'some string'
let b = 1
a = b /** Error! Type 'number' is not assignable to type 'string'. **/
//若是是複雜對象,TypeScript會查找最多見的類型
//推斷對象的類型。
const arr = [0, 1, false, true] // (number | boolean)[]
/** * 函數返回類型 */
function sum(x: number, y: number) {
return x + y // 推斷返回一個數字
}
複製代碼
類型兼容性基於結構類型,結構類型僅基於其成員關聯類型。 結構類型的基本規則 x 是與 yif y 至少具備相同成員的兼容 x。
interface Person {
name: string
}
let x: Person // Okay, 儘管不是Person接口的實現
let y = { name: 'John', age: 20 } // type { name: string; age: number }
x = y
//請注意x仍然是Person類型。
//在如下示例中,編譯器將顯示錯誤消息,由於它不會
//指望在Person中的屬性年齡,但結果將和預期的同樣
console.log(x.age) // 20
複製代碼
與 y 成員同樣 name: string,它匹配 Person 接口的必需屬性,這意味着它 x 是一個子類型 y。所以,容許分配。
在函數調用中,您須要傳入至少足夠的參數,這意味着額外的參數不會致使任何錯誤。
function consoleName(person: Person) {
console.log(person.name)
}
consoleName({ name: 'John' }) // Okay
consoleName({ name: 'John', age: 20 }) //也能夠是額外的參數
複製代碼
返回類型必須至少包含足夠的數據。
let x = () => ({ name: 'John' })
let y = () => ({ name: 'John', age: 20 })
x = y // OK
y = x /* Error! Property 'age' is missing in type '{ name: string; }' but required in type '{ name: string; age: number; }' */
複製代碼
類型守衛容許您縮小條件塊中對象的類型。
在條件塊中使用 typeof,編譯器將知道變量的類型是不一樣的。在下面的示例中,TypeScript 瞭解在條件塊以外,x 多是布爾值,而且 toFixed 沒法在其上調用該函數。
function example(x: number | boolean) {
if (typeof x === 'number') {
return x.toFixed(2)
}
return x.toFixed(2) // Error! Property 'toFixed' does not exist on type 'boolean'.
}
複製代碼
class MyResponse {
header = 'header example'
result = 'result example'
// ...
}
class MyError {
header = 'header example'
message = 'message example'
// ...
}
function example(x: MyResponse | MyError) {
if (x instanceof MyResponse) {
console.log(x.message) // Error! Property 'message' does not exist on type 'MyResponse'.
console.log(x.result) // Okay
} else {
// TypeScript knows this must be MyError
console.log(x.message) // Okay
console.log(x.result) // Error! Property 'result' does not exist on type 'MyError'.
}
}
複製代碼
該 in 操做員檢查的對象上的屬性的存在。
interface Person {
name: string
age: number
}
const person: Person = {
name: 'John',
age: 28
}
const checkForName = 'name' in person // true
複製代碼
文字是精確值,是 JavaScript 原語。它們能夠組合在一個類型聯合中以建立有用的抽象。
type Orientation = 'landscape' | 'portrait'
function changeOrientation(x: Orientation) {
// ...
}
changeOrientation('portrait') // Okay
changeOrientation('vertical') /* Error! Argument of type '"vertical"' is not assignable to parameter of type 'Orientation'. */
複製代碼
條件類型描述類型關係測試,並根據該測試的結果選擇兩種可能類型中的一種。
type X = A extends B ? C : D
複製代碼
這意味着若是 type A 能夠賦值給 type B,那麼 X 它的類型是 C。不然 X 與類型相同 D;
通用類型是必須包含或引用其餘類型才能完成的類型。它強制執行各類變量之間的有意義約束。 在如下示例中,函數返回您傳入的任何類型的數組。
function reverse<T>(items: T[]): T[] {
return items.reverse()
}
reverse([1, 2, 3]) // number[]
reverse([0, true]) // (number | boolean)[]
複製代碼
該 keyof 運營商查詢組給定類型的鑰匙。
interface Person {
name: string
age: number
}
type PersonKeys = keyof Person // 'name' | 'age'
複製代碼
映射類型容許您經過映射屬性類型從現有類型建立新類型。根據您指定的規則轉換現有類型的每一個屬性。
type Partial<T> = { [P in keyof T]?: T[P] }
複製代碼
正如咱們在 Interface 部分中所介紹的,TypeScript 容許您建立只讀屬性。有一種 Readonly 類型,它接受一個類型 T 並將其全部屬性設置爲只讀。
type Readonly<T> = { readonly [P in keyof T]: T[P] }
複製代碼
Exclude 容許您從其餘類型中刪除某些類型。Exclude 來自 T 任何可分配的東西 T。
/** * type Exclude<T, U> = T extends U ? never : T; */
type User = {
_id: number
name: string
email: string
created: number
}
type UserNoMeta = Exclude<keyof User, '_id' | 'created'>
複製代碼
Pick 容許您從其餘類型中選擇某些類型。Pick 來自 T 任何可分配的東西 T。
/** * type Pick<T, K extends keyof T> = { * [P in K]: T[P]; * }; */
type UserNoMeta = Pick<User, 'name' | 'email'>
複製代碼
您可使用 infer 關鍵字在 extends 條件類型的子句中推斷類型變量。此類推斷類型變量只能用於條件類型的 true 分支。
獲取函數的返回類型。
/** * 原始TypeScript的ReturnType * type ReturnType<T extends (...args: any) => any> = T extends (...args: any) => infer R ? R : any; */
type MyReturnType<T> = T extends (...args: any) => infer R ? R : any
type TypeFromInfer = MyReturnType<() => number> // number
type TypeFromFallback = MyReturnType<string> // any
複製代碼
分析一下 MyReturnType:
原文:www.freecodecamp.org/news/the-de…
參考:ts.xcatliu.com/ 翻譯不許確,或者難以理解的地方你們能夠指出,萬分感謝