最全TypeScript 入門基礎教程,看完就會

来源:https://www.cnblogs.com/coderhf/archive/2020/07/01/13220225.html
-Advertisement-
Play Games

想學習 TypeScript 的小伙伴看過來,本文將帶你一步步學習 TypeScript 入門相關的十四個知識點,詳細的內容大綱請看下圖: 一、TypeScript 是什麼 TypeScript 是一種由微軟開發的自由和開源的編程語言。它是 JavaScript 的一個超集,而且本質上向這個語言添加 ...


想學習 TypeScript 的小伙伴看過來,本文將帶你一步步學習 TypeScript 入門相關的十四個知識點,詳細的內容大綱請看下圖:

 

img

 

一、TypeScript 是什麼

 

TypeScript 是一種由微軟開發的自由和開源的編程語言。它是 JavaScript 的一個超集,而且本質上向這個語言添加了可選的靜態類型和基於類的面向對象編程。

 

TypeScript 提供最新的和不斷發展的 JavaScript 特性,包括那些來自 2015 年的 ECMAScript 和未來的提案中的特性,比如非同步功能和 Decorators,以幫助建立健壯的組件。下圖顯示了 TypeScript 與 ES5、ES2015 和 ES2016 之間的關係:

 

img

 

1.1 TypeScript 與 JavaScript 的區別

 

img

 

1.2 獲取 TypeScript

 

命令行的 TypeScript 編譯器可以使用 Node.js 包來安裝。

 

1.安裝 TypeScript

 

 $ npm install -g typescript

 

2.編譯 TypeScript 文件

 

 $ tsc helloworld.ts# helloworld.ts => helloworld.js

 

當然,對於剛入門 TypeScript 的小伙伴,也可以不用安裝 typescript,而是直接使用線上的 TypeScript Playground 來學習新的語法或新特性。

 

二、TypeScript 基礎類型

 

2.1 Boolean 類型

 

 let isDone: boolean = false;// ES5:var isDone = false;

 

2.2 Number 類型

 

 let count: number = 10;// ES5:var count = 10;

 

String 類型

 

 let name: string = "Semliker";// ES5:var name = 'Semlinker';

 

2.4 Array 類型

 

 let list: number[] = [1, 2, 3];// ES5:var list = [1,2,3];let list: Array<number> = [1, 2, 3]; // Array<number>泛型語法// ES5:var list = [1,2,3];

 

2.5 Enum 類型

 

使用枚舉我們可以定義一些帶名字的常量。 使用枚舉可以清晰地表達意圖或創建一組有區別的用例。 TypeScript 支持數字的和基於字元串的枚舉。

 

1.數字枚舉

 

 enum Direction {  NORTH,  SOUTH,  EAST,  WEST,}let dir: Direction = Direction.NORTH;

 

預設情況下,NORTH 的初始值為 0,其餘的成員會從 1 開始自動增長。換句話說,Direction.SOUTH 的值為 1,Direction.EAST 的值為 2,Direction.WEST 的值為 3。上面的枚舉示例代碼經過編譯後會生成以下代碼:

 

 
 enum Direction {
   NORTH,
   SOUTH,
   EAST,
   WEST,
 }
 
 let dir: Direction = Direction.NORTH;

 

當然我們也可以設置 NORTH 的初始值,比如:

 

 enum Direction {  NORTH = 3,  SOUTH,  EAST,  WEST,}

 

2.字元串枚舉

 

在 TypeScript 2.4 版本,允許我們使用字元串枚舉。在一個字元串枚舉里,每個成員都必須用字元串字面量,或另外一個字元串枚舉成員進行初始化。

 

 enum Direction {  NORTH = "NORTH",  SOUTH = "SOUTH",  EAST = "EAST",  WEST = "WEST",}

 

以上代碼對於的 ES5 代碼如下:

 

 
 "use strict";
 var Direction;
 (function (Direction) {
     Direction["NORTH"] = "NORTH";
     Direction["SOUTH"] = "SOUTH";
     Direction["EAST"] = "EAST";
     Direction["WEST"] = "WEST";
 })(Direction || (Direction = {}));

 

3.異構枚舉

 

異構枚舉的成員值是數字和字元串的混合:

 

 enum Enum {  A,  B,  C = "C",  D = "D",  E = 8,  F,}

 

以上代碼對於的 ES5 代碼如下:

 

 
 enum Enum {
   A,
   B,
   C = "C",
   D = "D",
   E = 8,
   F,
 }

 

通過觀察上述生成的 ES5 代碼,我們可以發現數字枚舉相對字元串枚舉多了 “反向映射”:

 

 console.log(Enum.A) //輸出:0console.log(Enum[0]) // 輸出:A

 

2.6 Any 類型

 

在 TypeScript 中,任何類型都可以被歸為 any 類型。這讓 any 類型成為了類型系統的頂級類型(也被稱作全局超級類型)。

 

 let notSure: any = 666;notSure = "Semlinker";notSure = false;

 

any 類型本質上是類型系統的一個逃逸艙。作為開發者,這給了我們很大的自由:TypeScript 允許我們對 any 類型的值執行任何操作,而無需事先執行任何形式的檢查。比如:

 

 let value: any;value.foo.bar; // OKvalue.trim(); // OKvalue(); // OKnew value(); // OKvalue[0][1]; // OK

 

在許多場景下,這太寬鬆了。使用 any 類型,可以很容易地編寫類型正確但在運行時有問題的代碼。如果我們使用 any 類型,就無法使用 TypeScript 提供的大量的保護機制。為瞭解決 any 帶來的問題,TypeScript 3.0 引入了 unknown 類型。

 

2.7 Unknown 類型

 

就像所有類型都可以賦值給 any,所有類型也都可以賦值給 unknown。這使得 unknown 成為 TypeScript 類型系統的另一種頂級類型(另一種是 any)。下麵我們來看一下 unknown 類型的使用示例:

 

 
 let value: unknown;
 
 value = true; // OK
 value = 42; // OK
 value = "Hello World"; // OK
 value = []; // OK
 value = {}; // OK
 value = Math.random; // OK
 value = null; // OK
 value = undefined; // OK
 value = new TypeError(); // OK
 value = Symbol("type"); // OK

 

value 變數的所有賦值都被認為是類型正確的。但是,當我們嘗試將類型為 unknown 的值賦值給其他類型的變數時會發生什麼?

 

 let value: unknown;let value1: unknown = value; // OKlet value2: any = value; // OKlet value3: boolean = value; // Errorlet value4: number = value; // Errorlet value5: string = value; // Errorlet value6: object = value; // Errorlet value7: any[] = value; // Errorlet value8: Function = value; // Error

 

unknown 類型只能被賦值給 any 類型和 unknown 類型本身。直觀地說,這是有道理的:只有能夠保存任意類型值的容器才能保存 unknown 類型的值。畢竟我們不知道變數 value 中存儲了什麼類型的值。

 

現在讓我們看看當我們嘗試對類型為 unknown 的值執行操作時會發生什麼。以下是我們在之前 any 章節看過的相同操作:

 

 
 let value: unknown;
 
 let value1: unknown = value; // OK
 let value2: any = value; // OK
 let value3: boolean = value; // Error
 let value4: number = value; // Error
 let value5: string = value; // Error
 let value6: object = value; // Error
 let value7: any[] = value; // Error
 let value8: Function = value; // Error

 

value 變數類型設置為 unknown 後,這些操作都不再被認為是類型正確的。通過將 any 類型改變為 unknown 類型,我們已將允許所有更改的預設設置,更改為禁止任何更改。

 

2.8 Tuple 類型

 

眾所周知,數組一般由同種類型的值組成,但有時我們需要在單個變數中存儲不同類型的值,這時候我們就可以使用元組。在 JavaScript 中是沒有元組的,元組是 TypeScript 中特有的類型,其工作方式類似於數組。

 

元組可用於定義具有有限數量的未命名屬性的類型。每個屬性都有一個關聯的類型。使用元組時,必須提供每個屬性的值。為了更直觀地理解元組的概念,我們來看一個具體的例子:

 

 let tupleType: [string, boolean];tupleType = ["Semlinker", true];

 

在上面代碼中,我們定義了一個名為 tupleType 的變數,它的類型是一個類型數組 [string, boolean],然後我們按照正確的類型依次初始化 tupleType 變數。與數組一樣,我們可以通過下標來訪問元組中的元素:

 

 console.log(tupleType[0]); // Semlinkerconsole.log(tupleType[1]); // true

 

在元組初始化的時候,如果出現類型不匹配的話,比如:

 

 tupleType = [true, "Semlinker"];

 

此時,TypeScript 編譯器會提示以下錯誤信息:

 

 [0]: Type 'true' is not assignable to type 'string'.[1]: Type 'string' is not assignable to type 'boolean'.

 

很明顯是因為類型不匹配導致的。在元組初始化的時候,我們還必須提供每個屬性的值,不然也會出現錯誤,比如:

 

 tupleType = ["Semlinker"];

 

此時,TypeScript 編譯器會提示以下錯誤信息:

 

 Property '1' is missing in type '[string]' but required in type '[string, boolean]'.

 

2.9 Void 類型

 

某種程度上來說,void 類型像是與 any 類型相反,它表示沒有任何類型。當一個函數沒有返回值時,你通常會見到其返回值類型是 void:

 

 // 聲明函數返回值為voidfunction warnUser(): void {  console.log("This is my warning message");}

 

以上代碼編譯生成的 ES5 代碼如下:

 

 "use strict";function warnUser() {  console.log("This is my warning message");}

 

需要註意的是,聲明一個 void 類型的變數沒有什麼作用,因為它的值只能為 undefinednull

 

 let unusable: void = undefined;

 

2.10 Null 和 Undefined 類型

 

TypeScript 里,undefinednull 兩者有各自的類型分別為 undefinednull

 

 let u: undefined = undefined;let n: null = null;

 

預設情況下 nullundefined 是所有類型的子類型。 就是說你可以把 nullundefined 賦值給 number 類型的變數。然而,如果你指定了--strictNullChecks 標記,nullundefined 只能賦值給 void 和它們各自的類型。

 

2.11 Never 類型

 

never 類型表示的是那些永不存在的值的類型。 例如,never 類型是那些總是會拋出異常或根本就不會有返回值的函數表達式或箭頭函數表達式的返回值類型。

 

 
 // 返回never的函數必須存在無法達到的終點
 function error(message: string): never {
   throw new Error(message);
 }
 
 function infiniteLoop(): never {
   while (true) {}
 }

 

在 TypeScript 中,可以利用 never 類型的特性來實現全面性檢查,具體示例如下:

 

 
 type Foo = string | number;
 
 function controlFlowAnalysisWithNever(foo: Foo) {
   if (typeof foo === "string") {
     // 這裡 foo 被收窄為 string 類型
  } else if (typeof foo === "number") {
     // 這裡 foo 被收窄為 number 類型
  } else {
     // foo 在這裡是 never
     const check: never = foo;
  }
 }

 

註意在 else 分支裡面,我們把收窄為 never 的 foo 賦值給一個顯示聲明的 never 變數。如果一切邏輯正確,那麼這裡應該能夠編譯通過。但是假如後來有一天你的同事修改了 Foo 的類型:

 

 type Foo = string | number | boolean;

 

然而他忘記同時修改 controlFlowAnalysisWithNever 方法中的控制流程,這時候 else 分支的 foo 類型會被收窄為 boolean 類型,導致無法賦值給 never 類型,這時就會產生一個編譯錯誤。通過這個方式,我們可以確保

 

controlFlowAnalysisWithNever 方法總是窮盡了 Foo 的所有可能類型。 通過這個示例,我們可以得出一個結論:使用 never 避免出現新增了聯合類型沒有對應的實現,目的就是寫出類型絕對安全的代碼。

 

三、TypeScript 斷言

 

有時候你會遇到這樣的情況,你會比 TypeScript 更瞭解某個值的詳細信息。通常這會發生在你清楚地知道一個實體具有比它現有類型更確切的類型。

 

通過類型斷言這種方式可以告訴編譯器,“相信我,我知道自己在乾什麼”。類型斷言好比其他語言里的類型轉換,但是不進行特殊的數據檢查和解構。它沒有運行時的影響,只是在編譯階段起作用。

 

類型斷言有兩種形式:

 

3.1 “尖括弧” 語法

 

 
 let someValue: any = "this is a string";
 let strLength: number = (<string>someValue).length;

 

3.2 as 語法

 

 
 let someValue: any = "this is a string";
 let strLength: number = (someValue as string).length;

 

四、類型守衛

 

A type guard is some expression that performs a runtime check that guarantees the type in some scope. —— TypeScript 官方文檔

 

類型保護是可執行運行時檢查的一種表達式,用於確保該類型在一定的範圍內。換句話說,類型保護可以保證一個字元串是一個字元串,儘管它的值也可以是一個數值。類型保護與特性檢測並不是完全不同,其主要思想是嘗試檢測屬性、方法或原型,以確定如何處理值。目前主要有四種的方式來實現類型保護:

 

4.1 in 關鍵字

 

 
 interface Admin {
   name: string;
   privileges: string[];
 }
 
 interface Employee {
   name: string;
   startDate: Date;
 }
 
 type UnknownEmployee = Employee | Admin;
 
 function printEmployeeInformation(emp: UnknownEmployee) {
   console.log("Name: " + emp.name);
   if ("privileges" in emp) {
     console.log("Privileges: " + emp.privileges);
  }
   if ("startDate" in emp) {
     console.log("Start Date: " + emp.startDate);
  }
 }

 

4.2 typeof 關鍵字

 

 
 function padLeft(value: string, padding: string | number) {
   if (typeof padding === "number") {
       return Array(padding + 1).join(" ") + value;
  }
   if (typeof padding === "string") {
       return padding + value;
  }
   throw new Error(`Expected string or number, got '${padding}'.`);
 }

 

typeof 類型保護只支持兩種形式:typeof v === "typename"typeof v !== typename"typename" 必須是 "number""string""boolean""symbol"。 但是 TypeScript 並不會阻止你與其它字元串比較,語言不會把那些表達式識別為類型保護。

 

4.3 instanceof 關鍵字

 

 
 interface Padder {
   getPaddingString(): string;
 }
 
 class SpaceRepeatingPadder implements Padder {
   constructor(private numSpaces: number) {}
   getPaddingString() {
     return Array(this.numSpaces + 1).join(" ");
  }
 }
 
 class StringPadder implements Padder {
   constructor(private value: string) {}
   getPaddingString() {
     return this.value;
  }
 }
 
 let padder: Padder = new SpaceRepeatingPadder(6);
 
 if (padder instanceof SpaceRepeatingPadder) {
   // padder的類型收窄為 'SpaceRepeatingPadder'
 }

 

4.4 自定義類型保護的類型謂詞

 

 
 function isNumber(x: any): x is number {
   return typeof x === "number";
 }
 
 function isString(x: any): x is string {
   return typeof x === "string";
 }

 

五、聯合類型和類型別名

 

5.1 聯合類型

 

聯合類型通常與 nullundefined 一起使用:

 

 const sayHello = (name: string | undefined) => {  /* ... */};

 

例如,這裡 name 的類型是 string | undefined 意味著可以將 stringundefined 的值傳遞給sayHello 函數。

 

 sayHello("Semlinker");sayHello(undefined);

 

通過這個示例,你可以憑直覺知道類型 A 和類型 B 聯合後的類型是同時接受 A 和 B 值的類型。

 

5.2 可辨識聯合

 

TypeScript 可辨識聯合(Discriminated Unions)類型,也稱為代數數據類型或標簽聯合類型。它包含 3 個要點:可辨識、聯合類型和類型守衛。

 

這種類型的本質是結合聯合類型和字面量類型的一種類型保護方法。如果一個類型是多個類型的聯合類型,且多個類型含有一個公共屬性,那麼就可以利用這個公共屬性,來創建不同的類型保護區塊。

 

1.可辨識

 

可辨識要求聯合類型中的每個元素都含有一個單例類型屬性,比如:

 

 
 enum CarTransmission {
   Automatic = 200,
   Manual = 300
 }
 
 interface Motorcycle {
   vType: "motorcycle"; // discriminant
   make: number; // year
 }
 
 interface Car {
   vType: "car"; // discriminant
   transmission: CarTransmission
 }
 
 interface Truck {
   vType: "truck"; // discriminant
   capacity: number; // in tons
 }

 

在上述代碼中,我們分別定義了 MotorcycleCarTruck 三個介面,在這些介面中都包含一個 vType 屬性,該屬性被稱為可辨識的屬性,而其它的屬性只跟特性的介面相關。

 

2.聯合類型

 

基於前面定義了三個介面,我們可以創建一個 Vehicle 聯合類型:

 

 type Vehicle = Motorcycle | Car | Truck;

 

現在我們就可以開始使用 Vehicle 聯合類型,對於 Vehicle 類型的變數,它可以表示不同類型的車輛。

 

3.類型守衛

 

下麵我們來定義一個 evaluatePrice 方法,該方法用於根據車輛的類型、容量和評估因數來計算價格,具體實現如下:

 

 
 const EVALUATION_FACTOR = Math.PI;
 function evaluatePrice(vehicle: Vehicle) {
   return vehicle.capacity * EVALUATION_FACTOR;
 }
 
 const myTruck: Truck = { vType: "truck", capacity: 9.5 };
 evaluatePrice(myTruck);

 

對於以上代碼,TypeScript 編譯器將會提示以下錯誤信息:

 

 
 Property 'capacity' does not exist on type 'Vehicle'.
 Property 'capacity' does not exist on type 'Motorcycle'.

 

原因是在 Motorcycle 介面中,並不存在 capacity 屬性,而對於 Car 介面來說,它也不存在 capacity 屬性。那麼,現在我們應該如何解決以上問題呢?這時,我們可以使用類型守衛。下麵我們來重構一下前面定義的 evaluatePrice 方法,重構後的代碼如下:

 

 
 function evaluatePrice(vehicle: Vehicle) {
   switch(vehicle.vType) {
     case "car":
       return vehicle.transmission * EVALUATION_FACTOR;
     case "truck":
       return vehicle.capacity * EVALUATION_FACTOR;
     case "motorcycle":
       return vehicle.make * EVALUATION_FACTOR;
  }
 }

 

在以上代碼中,我們使用 switchcase 運算符來實現類型守衛,從而確保在 evaluatePrice 方法中,我們可以安全地訪問 vehicle 對象中的所包含的屬性,來正確的計算該車輛類型所對應的價格。

 

5.3 類型別名

 

類型別名用來給一個類型起個新名字。

 

 type Message = string | string[];let greet = (message: Message) => {  // ...};

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 想要實現側邊欄,需要配合使用DrawerLayout。因為會用到嵌套佈局,所以根佈局不能是 ConstraintLayout,DrawerLayout 佈局下再嵌套兩個佈局,一個放置內容如 LinearLayout,一個放置側劃菜單,這裡使用 NavigationView 為了出現側邊欄點擊圖標和菜... ...
  • 前言: 最近在整理自己的技術棧,收集了一些自己認為比較重要的知識點分享給大家。 Runloop 1. iOS中觸摸事件傳遞和響應原理 2. 為什麼只有主線程的runloop是開啟的 3. 為什麼只在主線程刷新UI 4. PerformSelector和runloop的關係 5.GCD 在Runloo ...
  • 一、數組遍歷和其他 1.合併數組 數組.concat(數組1,數組2,數組3,...) 可以用來合併多個數組 //合併多個數組concat var arr = ["zhangsan","lisi","wangwu"]; var arr1 = ["zhaoli","liqi"]; var arr2 = ...
  • 頁面強行註入jQuery這個腳手架,然後可以用jQuery幹些事情了。 ...
  • 課程開始,今天學習了html的基本標簽: div + span: div和span兩個是雙標簽,html中有雙標簽和單標簽,下文中有單標簽介紹 ,雙標簽成雙成對出現例如: <div> <span></span> </div> 有<div>必定要有</div>這就是雙標簽的特性。 div和span沒有 ...
  • 觀察自然界中樹的分叉,一根主幹生長出兩個側乾,每個側乾又長出兩個側乾,以此類推,便生長出疏密有致的結構。這樣的生長結構,使用遞歸演算法可以模擬出來。 例如,分叉的側乾按45°的偏轉角度進行生長的遞歸示意圖如圖1所示。 圖1 生成樹的遞歸示意圖 按照樹分叉生長側乾的遞歸思想,編寫如下的HTML代碼。 < ...
  • 1 <!DOCTYPE html> 2 <html> 3 <head> 4 <meta charset="utf-8"> 5 <title>JavaScript中的運算符</title> 6 </head> 7 <body> 8 <script type="text/javascript"> 9 / ...
  • 1 <!DOCTYPE html> 2 <html> 3 <head> 4 <meta charset="utf-8"> 5 <title>JavaScript簡介</title> 6 </head> 7 <body> 8 <script type="text/javascript"> 9 wind ...
一周排行
    -Advertisement-
    Play Games
  • .Net8.0 Blazor Hybird 桌面端 (WPF/Winform) 實測可以完整運行在 win7sp1/win10/win11. 如果用其他工具打包,還可以運行在mac/linux下, 傳送門BlazorHybrid 發佈為無依賴包方式 安裝 WebView2Runtime 1.57 M ...
  • 目錄前言PostgreSql安裝測試額外Nuget安裝Person.cs模擬運行Navicate連postgresql解決方案Garnet為什麼要選擇Garnet而不是RedisRedis不再開源Windows版的Redis是由微軟維護的Windows Redis版本老舊,後續可能不再更新Garne ...
  • C#TMS系統代碼-聯表報表學習 領導被裁了之後很快就有人上任了,幾乎是無縫銜接,很難讓我不想到這早就決定好了。我的職責沒有任何變化。感受下來這個系統封裝程度很高,我只要會調用方法就行。這個系統交付之後不會有太多問題,更多應該是做小需求,有大的開發任務應該也是第二期的事,嗯?怎麼感覺我變成運維了?而 ...
  • 我在隨筆《EAV模型(實體-屬性-值)的設計和低代碼的處理方案(1)》中介紹了一些基本的EAV模型設計知識和基於Winform場景下低代碼(或者說無代碼)的一些實現思路,在本篇隨筆中,我們來分析一下這種針對通用業務,且只需定義就能構建業務模塊存儲和界面的解決方案,其中的數據查詢處理的操作。 ...
  • 對某個遠程伺服器啟用和設置NTP服務(Windows系統) 打開註冊表 HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\W32Time\TimeProviders\NtpServer 將 Enabled 的值設置為 1,這將啟用NTP伺服器功 ...
  • title: Django信號與擴展:深入理解與實踐 date: 2024/5/15 22:40:52 updated: 2024/5/15 22:40:52 categories: 後端開發 tags: Django 信號 松耦合 觀察者 擴展 安全 性能 第一部分:Django信號基礎 Djan ...
  • 使用xadmin2遇到的問題&解決 環境配置: 使用的模塊版本: 關聯的包 Django 3.2.15 mysqlclient 2.2.4 xadmin 2.0.1 django-crispy-forms >= 1.6.0 django-import-export >= 0.5.1 django-r ...
  • 今天我打算整點兒不一樣的內容,通過之前學習的TransformerMap和LazyMap鏈,想搞點不一樣的,所以我關註了另外一條鏈DefaultedMap鏈,主要調用鏈為: 調用鏈詳細描述: ObjectInputStream.readObject() DefaultedMap.readObject ...
  • 後端應用級開發者該如何擁抱 AI GC?就是在這樣的一個大的浪潮下,我們的傳統的應用級開發者。我們該如何選擇職業或者是如何去快速轉型,跟上這樣的一個行業的一個浪潮? 0 AI金字塔模型 越往上它的整個難度就是職業機會也好,或者說是整個的這個運作也好,它的難度會越大,然後越往下機會就會越多,所以這是一 ...
  • @Autowired是Spring框架提供的註解,@Resource是Java EE 5規範提供的註解。 @Autowired預設按照類型自動裝配,而@Resource預設按照名稱自動裝配。 @Autowired支持@Qualifier註解來指定裝配哪一個具有相同類型的bean,而@Resourc... ...