1. 程式人生 > 實用技巧 >TypeScript:入門TS

TypeScript:入門TS

什麼是 TypeScript

官方網站的定義是:TypeScript 是js型別的超集。它假設咱們知道什麼是超集,什麼是型別化。為了簡單起見,你可以將TypeScript 看作是JavaScript之上的一個外殼

TypeScript是一個外殼,因為編寫 TypeScript 的程式碼,在編譯之後,,剩下的只是簡單的js程式碼。

但是 JS 引擎無法讀取 TypeScript 程式碼,因此任何 TypeScript 檔案都應該經過預翻譯過程,即編譯。只有在第一個編譯步驟之後,才剩下純 JS 程式碼,可以在瀏覽器中執行。稍後會介紹 TypeScript 編譯是如何完成的。

現在讓我們記住 TypeScript 是一種特殊的 JS,在瀏覽器中執行之前它需要一個翻譯。

為什麼要使用 TypeScript

剛開始,咱們不完全理解 TypeScript 為何有意義。 你可能會問“ TypeScript 的目的是什麼”。 這是一個很好的問題。

實際上,一旦它在您的程式碼中發現嚴重和愚蠢的錯誤,你就會看到 TypeScript 的好處。更重要的是,TypeScript 會讓程式碼變得結構良好而且還是自動,這些還只是 TypeScript 的一小部分。

不管怎樣,也經常有人說 TypeScript 沒用,太過笨拙。

凡事都有兩面性,TypeScript 有很多反對者和支持者,但重要的是 TypeScript 是一個可靠的工具,將它放在咱們的工具技能包中不會造成傷害。

TypeScript 配置

為什麼配置? TypeScript 還有一個二進位制檔案,可將 TypeScript 程式碼編譯為 JS 程式碼. 請記住,瀏覽器不理解 TypeScript:

mkdir typescript-tutorial && cd $_
npm init -y

然後安裝 TypeScript

npm i typescript --save-dev

接下來在package.json中的scripts下新增如下內容,以便咱們可以輕鬆地執行 TypeScript 編譯器:

  "scripts": {
    "tsc": "tsc"
  }

tsc 代表 TypeScript 編譯器

,只要編譯器執行,它將在專案資料夾中查詢名為tsconfig.json的檔案。 使用以下命令為 TypeScript 生成配置檔案:

npm run tsc -- --init

執行成功後會在控制檯收到message TS6071: Successfully created a tsconfig.json file。在專案資料夾中會看到新增了一個tsconfig.json檔案。tsconfig。json是一個可怕的配置檔案,不要慌。咱們不需要知道它的每一個要點,在下一節中,會介紹入門的相關部分。

配置TypeScript 編譯器

最好先初始化 git repo 並提交原始的tsconfig.json,然後再開啟檔案。 我們將只保留一些配置選項,並刪除其他所有內容。 稍後,你可能需要將現在的版本與原始版本進行比較。

首先,請開啟tsconfig.json並將所有原始內容替換為以下內容:

{
  "compilerOptions": {
    "target": "es5",
    "strict": true
  }
}

儲存並關閉檔案。 首先,你可能想知道tsconfig.json是幹什麼的。 該配置檔案由 TypeScript 編譯器和任何具有 TypeScript 支援的程式碼編輯器讀取。

  • noImplicitAnytrue:當變數沒有定義型別時,TypeScript 會報錯
  • alwaysStricttrue:嚴格模式是 JS 的安全機制,它可以防止意外的全域性變數,預設的this繫結等。 設定為 “alwaysStrict” 時,TypeScript 在每個KS 檔案的頂部都使用“use strict”。

有更多的配置選項可用。隨著時間的推移,你會學到更多,因為現在上面的兩個選擇是你開始學習時需要知道的一切。

關於型別的幾個詞

TypeScript 支援與 JS 幾乎相同的資料型別,此外,TypeScript 自己添加了更多的型別,如any型別一樣。

“any”是鬆散的 TypeScript 型別。 這意味著:此變數可以是任何型別:字串,布林值,物件等。 實際上,這就像根本沒有型別檢查。

TypeScript 中的行為

咱們從一個合法的 KS函式開始:filterByTerm。在專案資料夾中建立一個名為filterByTerm.js的新檔案,並輸入以下內容

function filterByTerm(input, searchTerm) {
  if (!searchTerm) throw Error("searchTerm 不能為空");
  if (!input.length) throw Error("input 不能為空");
  const regex = new RegExp(searchTerm, "i");
  return input.filter(function(arrayElement) {
    return arrayElement.url.match(regex);
  });
}

filterByTerm("input string", "java");

如果現在不瞭解邏輯,請不要擔心。 看一下該函式的引數,以及幾行之後如何使用它們。 只需檢視程式碼,就應該已經發現了問題。

我想知道是否有一種方法可以在我的 IDE 中檢查這個函式,而不需要執行程式碼或者用Jest測試它。這可能嗎? TypeScript 在這方面做得很好,實際上它是 JS 中靜態檢查的最佳工具,也就是說,在程式碼執行之前測試程式碼的正確性

因此,咱們改用 TypeScript ,將檔案的副檔名從filterByTerm.js改為filterByTerm.ts。通過這種更改,你會發現程式碼中的一堆錯誤

可以看到函式引數下的有很多紅色標記。從現在開始,會向你展示文字形式的錯誤,但是請記住,當咱們在TypeScript 中出錯時,IDE 和文字編輯器都會顯示這些紅線。

確定哪個地方錯:

npm run tsc

可以看到控制的報錯:

filterByTerm.ts:1:23 - error TS7006: Parameter 'input' implicitly has an 'any' type.

1 function filterByTerm(input, searchTerm) {
                        ~~~~~

filterByTerm.ts:1:30 - error TS7006: Parameter 'searchTerm' implicitly has an 'any' type.

1 function filterByTerm(input, searchTerm) {
                               ~~~~~~~~~~

filterByTerm.ts:5:32 - error TS7006: Parameter 'arrayElement' implicitly has an 'any' type.

5   return input.filter(function(arrayElement) {

TypeScript 告訴你函式引數具有“any”型別,如果還記得的話,它可以是 TypeScript 中的any型別。 我們需要在我們的 TypeScript 程式碼中新增適當的型別註釋。

什麼是型別,JS 中有什麼問題

到目前為止,JS 有七種型別

  • String
  • Number
  • Boolean
  • Null
  • Undefined
  • Object
  • Symbol (ES6)

除了 Object 型別外,其它是 JS 的基本資料型別。每種 JS 型別都有相應的表示,可以程式碼中使用,比如字串和數字

var name = "Hello John";
var age = 33;

JS 的問題是,變數可以隨時更改其型別。例如,布林值可以變成字串(將以下程式碼儲存到名為types.js的檔案中)

var aBoolean = false;
console.log(typeof aBoolean); // "boolean"

aBoolean = "Tom";
console.log(typeof aBoolean); // "string"

轉換可以是有意的,開發人員可能真的希望將Tom分配到aBoolean,但是這類錯誤很可能是偶然發生的。

從技術上講,JS 本身沒有什麼問題,因為它的型別動態是故意的。JS 是作為一種簡單的 web 指令碼語言而誕生的,而不是作為一種成熟的企業語言。

然而,JS 鬆散的特性可能會在你的程式碼中造成嚴重的問題,破壞其可維護性。TypeScript 旨在通過向 JS 新增強型別來解決這些問題。實際上,如果將types.js的擴充套件更改為types.ts。你會在IDE中看到 TypeScript 的抱怨。

types.ts的編譯控制檯會報錯:

types.ts:4:1 - error TS2322: Type '"Tom"' is not assignable to type 'boolean'.

有了這些知識,接著,咱們更深入地研究 TypeScript 型別。

深入 TypeScript 型別

TypeScript 強調有型別,咱們上面的程式碼根本沒有型別,是時候新增一些了。首先要修正函式引數。通過觀察這個函式是如何呼叫的,它似乎以兩個字串作為引數:

filterByTerm("input string", "java");

為引數新增型別:

function filterByTerm(input: string, searchTerm: string) {
    // ...
}

// ...

接著編譯:

npm run tsc

剩下的錯誤:

filterByTerm.ts:5:16 - error TS2339: Property 'filter' does not exist on type 'string'.

可以看到 TypeScript 是如何指導我們,現在的問題在於filter方法。

function filterByTerm(input: string, searchTerm: string) {
  // 省略一些
  return input.filter(function(arrayElement) {
    return arrayElement.url.match(regex);
  });
}

咱們告訴 TypeScript“input”是一個字串,但是在後面的程式碼中呼叫了filter方法,它屬於陣列。我們真正需要的是將輸入標記為某個東西的陣列,可能是字串陣列:

為此,有兩個選擇。選項1:string[]

function filterByTerm(input: string[], searchTerm: string) {
    // ...
}

選項2:Array<Type>

function filterByTerm(input: Array<string>, searchTerm: string) {
    // ...

}

我個人更喜歡選項2。 現在,嘗試再次編譯(npm run tsc),控制檯資訊如下:

filterByTerm.ts:10:14 - error TS2345: Argument of type '"input string"' is not assignable to parameter of type 'string[]'.

filterByTerm("input string", "java");

TypeScript 還會校驗傳入的型別。 我們將input改為字串陣列:

filterByTerm(["string1", "string2", "string3"], "java");

這是到目前為止的完整程式碼:

function filterByTerm(input: Array<string>, searchTerm: string) {
  if (!searchTerm) throw Error("searchTerm 不能為空");
  if (!input.length) throw Error("input 不能為空");
  const regex = new RegExp(searchTerm, "i");
  return input.filter(function(arrayElement) {
    return arrayElement.url.match(regex);
  });
}

filterByTerm(["string1", "string2", "string3"], "java");

看上去很好,但是,編譯(npm run tsc)還是過不了:

filterByTerm.ts:6:25 - error TS2339: Property 'url' does not exist on type 'string'.

TypeScript 確實很嚴謹。 我們傳入了一個字串陣列,但是在程式碼後面,嘗試訪問一個名為“url”的屬性:

return arrayElement.url.match(regex);

這意味著咱們需要一個物件陣列,而不是字串陣列。咱們在下一節中解決這個問題。

TypeScript 物件和介面

上面遺留一個問題:因為filterByTerm被傳遞了一個字串陣列。url屬性在型別為string的TypeScript 上不存在。所以咱們改用傳遞一個物件陣列來解決這個問題:

filterByTerm(
  [{ url: "string1" }, { url: "string2" }, { url: "string3" }],
  "java"
);

函式定義也要對應的更改:

function filterByTerm(input: Array<object>, searchTerm: string) {
    // omitted
}

現在讓我們編譯程式碼

npm run tsc

控制輸出:

filterByTerm.ts:6:25 - error TS2339: Property 'url' does not exist on type 'object'.

又來了,通用 JS 物件沒有任何名為url的屬性。對我來說,TypeScript 對型別要求真的是很嚴謹。

這裡的問題是,咱們不能給一個隨機物件分配屬性,TypeScript 的核心原則之一是對值所具有的結構進行型別檢查, 在 TypeScript 裡,介面(interface)的作用就是為這些型別命名和為你的程式碼或第三方程式碼定義契約,咱們可以使用介面來解決這個問題。

通過檢視我們的程式碼,我們可以想到一個名為Link的簡單"模型",其結構應該符合以下模式:它必須有一個型別為string的url屬性。

在TypeScript 中,你可以用一個介面來定義這個模型,就像這樣(把下面的程式碼放在filterByTerm.ts的頂部):

interface ILink {
  url: string;
}

對於介面宣告,這當然不是有效的 JS語法,在編譯過程中會被刪除。

提示:在定義介面名字前面加上大寫的I,這是 TypeScript 的慣例。

現在,使用使用介面ILink定義input型別

function filterByTerm(input: Array<ILink>, searchTerm: string) {
    // ...
}

通過此修復,可以說 TypeScript “期望ILink陣列”作為該函式的輸入,以下是完整的程式碼:

interface ILink {
  url: string;
}

function filterByTerm(input: Array<ILink>, searchTerm: string) {
  if (!searchTerm) throw Error("searchTerm 不能為空");
  if (!input.length) throw Error("input 不能為空");
  const regex = new RegExp(searchTerm, "i");
  return input.filter(function(arrayElement) {
    return arrayElement.url.match(regex);
  });
}

filterByTerm(
  [{ url: "string1" }, { url: "string2" }, { url: "string3" }],
  "java"
);

此時,所有的錯誤都應該消失了,可以運行了:

npm run tsc

編譯後,會在專案資料夾中生成一個名為filterByTerm.js的檔案,其中包含純 JS 程式碼。可以檢出該檔案並檢視 TypeScript 特定的宣告最終轉換成 JS 的是什麼樣的。

因為alwaysStrict設定為true,所以 TypeScript 編譯器也會在filterByTerm.js的頂部使用use strict。

介面和欄位

TypeScript 介面是該語言最強大的結構之一。介面有助於在整個應用程式中形成模型,這樣任何開發人員在編寫程式碼時都可以選擇這種模型並遵循它。

前面,咱們定義了一個簡單的介面ILink

interface ILink {
  url: string;
}

如果您想要向介面新增更多的欄位,只需在塊中宣告它們即可:

interface ILink {
  description: string;
  id: number;
  url: string;
}

現在,型別為ILink的物件都必須實現新欄位,否則就會出現錯誤,如果把上面 的定義重新寫入filterByTerm.ts然後重新編譯就會報錯了:

filterByTerm.ts:17:4 - error TS2739: Type '{ url: string; }' is missing the following properties from type 'ILink': description, id

問題在於我們函式的引數:

filterByTerm(
  [{ url: "string1" }, { url: "string2" }, { url: "string3" }],
  "java"
);

TypeScript 可以通過函式宣告來推斷引數是ILink的型別陣列。因此,該陣列中的任何物件都必須實現介面ILink中定義的所有欄位

大多數情況下,實現所有欄位是不太現實的。畢竟,咱也不知道ILink型別的每個新物件是否會需要擁有所有欄位。不過不要擔心,要使編譯通過,可以宣告介面的欄位可選,使用?表示:

interface ILink {
  description?: string;
  id?: number;
  url: string;
}

現在編輯器和編譯器都沒問題了。然而 TypeScript 介面可以做的更多,在下一節我們將看到如何擴充套件它們。但首先簡要介紹一下 TypeScript 中的變數。

變數宣告

到目前為止,咱們已經瞭解瞭如何向函式引數中新增型別:

function filterByTerm(input: Array<ILink>, searchTerm: string) {
    //
}

TypeScript 並不限於此,當然也可以向任何變數新增型別。為了說明這個例子,咱們一一地提取函式的引數。首先咱要提取每一個單獨的物件:

const obj1: ILink = { url: "string1" };
const obj2: ILink = { url: "string2" };
const obj3: ILink = { url: "string3" };

接下來我們可以像這樣定義一個ILink陣列:

const arrOfLinks: Array<ILink> = [obj1, obj2, obj3];

引數searchTerm對應的型別可以這樣:

const term: string = "java";

以下是完整的程式碼:

interface ILink {
  description?: string;
  id?: number;
  url: string;
}

function filterByTerm(input: Array<ILink>, searchTerm: string) {
  if (!searchTerm) throw Error("searchTerm 不能為空");
  if (!input.length) throw Error("input 不能為空");
  const regex = new RegExp(searchTerm, "i");
  return input.filter(function(arrayElement) {
    return arrayElement.url.match(regex);
  });
}

const obj1: ILink = { url: "string1" };
const obj2: ILink = { url: "string2" };
const obj3: ILink = { url: "string3" };

const arrOfLinks: Array<ILink> = [obj1, obj2, obj3];

const term: string = "java";

filterByTerm(arrOfLinks, term);

與 JS 相比,TypeScript看起來更冗長,有時甚至是多餘的。但是隨著時間的推移,會發現新增的型別越多,程式碼就越健壯。

通過新增型別註釋,對 TypeScript 的瞭解也越多,還可以幫助你更好地理解程式碼的意圖。

例如,arrOfLinks與正確的型別(ILink的陣列)相關聯,咱們編輯器就可以推斷出陣列中的每個物件都有一個名為url的屬性,如介面ILink中所定義:

除了字串、陣列和數字之外,TypeScript 還有更多型別。有boolean,tuple (元組)any,never,enum。如果你感興趣,可以檢視文件。

現在,咱們繼續擴充套件介面。

擴充套件介面

TypeScript 介面很好。但是,如果哪天咱們需要一個新的物件,所需的型別跟現在有介面基本差不多。假設我們需要一個名為IPost的新介面,它具有以下屬性:

  • id, number
  • title, string
  • body, string
  • url, string
  • description, string

該介面的欄位其中有些,我們ILink介面都有了。

interface ILink {
  description?: string;
  id?: number;
  url: string;
}

是否有辦法重用介面ILink? 在 TypeScript 中,可以使用繼承來擴充套件介面,關鍵字用extends表示:

interface ILink {
  description?: string;
  id?: number;
  url: string;
}

interface IPost extends ILink {
  title: string;
  body: string;
}

現在,IPost型別的物件都將具有可選的屬性description、id、url和必填的屬性title和body:

interface ILink {
  description?: string;
  id?: number;
  url: string;
}

interface IPost extends ILink {
  title: string;
  body: string;
}

const post1: IPost = {
  description:
    "TypeScript tutorial for beginners is a tutorial for all the JavaScript developers ...",
  id: 1,
  url: "www.valentinog.com/typescript/",
  title: "TypeScript tutorial for beginners",
  body: "Some stuff here!"
};

當像post1這樣的物件使用一個介面時,我們說post1實現了該介面中定義的屬性。

擴充套件介面意味著借用其屬性並擴充套件它們以實現程式碼重用。當然 TypeScript 介面還也可以描述函式,稍後會看到。

索引

JS 物件是鍵/值對的容器。 如下有一個簡單的物件:

const paolo = {
  name: "Paolo",
  city: "Siena",
  age: 44
};

我們可以使用點語法訪問任何鍵的值:

console.log(paolo.city);

現在假設鍵是動態的,我們可以把它放在一個變數中,然後在括號中引用它

const paolo = {
  name: "Paolo",
  city: "Siena",
  age: 44
};

const key = "city";

console.log(paolo[key]);

現在咱們新增另一個物件,將它們都放到一個數組中,並使用filter方法對陣列進行篩選,就像我們在filterByTerm.js中所做的那樣。但這一次是動態傳遞的,因此可以過濾任何物件

const paolo = {
  name: "Paolo",
  city: "Siena",
  age: 44
};

const tom = {
  name: "Tom",
  city: "Munich",
  age: 33
};

function filterPerson(arr, term, key) {
  return arr.filter(function(person) {
    return person[key].match(term);
  });
}

filterPerson([paolo, tom], "Siena", "city");

這是比較重要的一行行:

return person[key].match(term);

能行嗎 是的,因為 JS 不在乎paolo或tom是否可通過動態[key]進行“索引化”。 那在 TS 又是怎麼樣的呢?

在下一部分中,我們將使用動態鍵使filterByTerm更加靈活。

介面可以有索引

讓我們回到filterByTerm.tsfilterByTerm函式

function filterByTerm(input: Array<ILink>, searchTerm: string) {
  if (!searchTerm) throw Error("searchTerm 不能為空");
  if (!input.length) throw Error("input 不能為空");
  const regex = new RegExp(searchTerm, "i");
  return input.filter(function(arrayElement) {
    return arrayElement.url.match(regex);
  });
}

它看起來不那麼靈活,因為對於每個ILink,咱們都使用硬編碼方式將屬性url與正則表示式相匹配。我們希望使動態屬性(也就是鍵)讓程式碼更靈活:

function filterByTerm(
  input: Array<ILink>,
  searchTerm: string,
  lookupKey: string = "url"
) {
  if (!searchTerm) throw Error("searchTerm 不能為空");
  if (!input.length) throw Error("input 不能為空");
  const regex = new RegExp(searchTerm, "i");
  return input.filter(function(arrayElement) {
    return arrayElement[lookupKey].match(regex);
  });
}

lookupKey是動態鍵,這是給它分配了預設引數“url”。 接著編譯程式碼:

npm run tsc

當然會報錯:

error TS7053: Element implicitly has an 'any' type because expression of type 'string' can't be used to index type 'ILink'.
  No index signature with a parameter of type 'string' was found on type 'ILink'.

出錯行:

return arrayElement[lookupKey].match(regex);

元素隱式具有"any"型別,因為型別“ILink”沒有索引簽名,需要你新增一個索引到物件的介面,這很容易解決。

轉到介面ILink並新增索引:

interface ILink {
  description?: string;
  id?: number;
  url: string;
  [index: string] : string
}

語法有點奇怪,但類似於物件上的動態鍵訪問。這意味著我們可以通過型別為string的索引訪問該物件的任何鍵,該索引反過來又返回另一個字串。

不過,這樣寫會引發其它錯誤:

error TS2411: Property 'description' of type 'string | undefined' is not assignable to string index type 'string'.
error TS2411: Property 'id' of type 'number | undefined' is not assignable to string index type 'string'.

這是因為介面上的一些屬性是可選的,可能是undefined,而且返回型別不總是string(例如,id 是一個number)。

interface ILink {
  description?: string;
  id?: number;
  url: string;
  [index: string]: string | number | undefined;
}

這一行:

[index: string]: string | number | undefined;

表示該索引是一個字串,可以返回另一個字串、數字或undefined。嘗試再次編譯,這裡有另一個錯誤

error TS2339: Property 'match' does not exist on type 'string | number'.
return arrayElement[lookupKey].match(regex);

報的沒毛病。match方法只存在字串中 ,而且我們的索引有可能返回一個number。為了修正這個錯誤,我們可以使用any型別:

interface ILink {
  description?: string;
  id?: number;
  url: string;
  [index: string]: any;
}

再次編譯通過。

函式的返回型別

到目前為止有很多新東西。現在來看看 TypeScript 的另一個有用的特性:函式的返回型別

回到filterByTerm函式:

function filterByTerm(
  input: Array<ILink>,
  searchTerm: string,
  lookupKey: string = "url"
) {
  if (!searchTerm) throw Error("searchTerm 不能為空");
  if (!input.length) throw Error("input 不能為空");
  const regex = new RegExp(searchTerm, "i");
  return input.filter(function(arrayElement) {
    return arrayElement[lookupKey].match(regex);
  });
}

如果按原樣呼叫,傳遞前面看到的ILink陣列和搜尋詞string3,則如預期的那樣返回一個物件陣列:

filterByTerm(arrOfLinks, "string3"); 

// EXPECTED OUTPUT:
// [ { url: 'string3' } ]

但現在考慮一個更改的變體:

function filterByTerm(
  input: Array<ILink>,
  searchTerm: string,
  lookupKey: string = "url"
) {
  if (!searchTerm) throw Error("searchTerm cannot be empty");
  if (!input.length) throw Error("input cannot be empty");
  const regex = new RegExp(searchTerm, "i");
  return input
    .filter(function(arrayElement) {
      return arrayElement[lookupKey].match(regex);
    })
    .toString();
}

如果現在呼叫,使用相同的ILink陣列和搜尋詞string3,它將返回[object object]

filterByTerm(arrOfLinks, "string3");

// WRONG OUTPUT:
// [object Object]

該函式沒有按照預期工作,如果對 JS 隱式型別轉換不清楚就很難發現問題。幸運的是,TypeScript 可以捕獲這些錯誤,就像你在編輯器中寫的那樣。

修正如下:

function filterByTerm(/* 省略 */): Array<ILink> {
 /* 省略 */
}

它是如何工作的? 通過在函式體之前新增型別註釋,告訴 TypeScript 期望另一個數組作為返回值。現在這個bug 很容易被發現。

interface ILink {
  description?: string;
  id?: number;
  url: string;
  [index: string]: any;
}

function filterByTerm(
  input: Array<ILink>,
  searchTerm: string,
  lookupKey: string = "url"
): Array<ILink> {
  if (!searchTerm) throw Error("searchTerm cannot be empty");
  if (!input.length) throw Error("input cannot be empty");
  const regex = new RegExp(searchTerm, "i");
  return input
    .filter(function(arrayElement) {
      return arrayElement[lookupKey].match(regex);
    })
    .toString();
}

const obj1: ILink = { url: "string1" };
const obj2: ILink = { url: "string2" };
const obj3: ILink = { url: "string3" };

const arrOfLinks: Array<ILink> = [obj1, obj2, obj3];

filterByTerm(arrOfLinks, "string3");

現在編譯並檢查錯誤:

error TS2322: Type 'string' is not assignable to type 'ILink[]'.

咱們希望返回值的是ILink陣列,而不是字串。要修復此錯誤,從末尾刪除.tostring()並重新編譯程式碼就行了。

類型別名 vs 介面

到目前為止,我們已經將介面視為描述物件和自定義型別的工具。但是通過其他人的程式碼,你可能也注意到了關鍵字的type

顯然,interface和 type 在 TypeScript 中可以互換使用,但是它們在許多方面有所不同,這就是TypeScript 給初學者的困惑。

請記住: TypeScript 中的介面描述是某個東西的結構,大多數情況下是一個複雜的物件。

另一方面,type也可以用來描述自定義的結構,但它只是一個別名,或者換句話說,是自定義型別的標籤。例如,設想一個有兩個欄位的介面,其中一個是布林型、數字型和字串型的聯合型別

interface IExample {
  authenticated: boolean | number | string;
  name: string;
}

例如,使用type 別名可以提取自定義聯合型別,並建立名為Authenticated的標籤

type Authenticated = boolean | number | string;

interface IExample {
  authenticated: Authenticated;
  name: string;
}

通過這種方式,咱可以隔離所做的更改,就不必在整個程式碼庫中複製/貼上聯合型別

如果要將type應用上面示例(filterByTerm),建立一個名為ILinks的新標籤,並將Array <ILink>分配給它。 這樣,就可以引用前者:

// the new label
type ILinks = Array<ILink>;
// the new label

function filterByTerm(
  input: ILinks,
  searchTerm: string,
  lookupKey: string = "url"
): ILinks {
  if (!searchTerm) throw Error("searchTerm 不能為空");
  if (!input.length) throw Error("input 不能為空");
  const regex = new RegExp(searchTerm, "i");
  return input.filter(function(arrayElement) {
    return arrayElement[lookupKey].match(regex);
  });
}

const obj1: ILink = { url: "string1" };
const obj2: ILink = { url: "string2" };
const obj3: ILink = { url: "string3" };

const arrOfLinks: ILinks = [obj1, obj2, obj3];

filterByTerm(arrOfLinks, "string3");

當然,這不是type用法最好事例。那麼在interfacetype之間使用哪個呢? 我更喜歡複雜物件的介面。TypeScript 文件也建議了。

一個軟體的理想狀態是可以擴充套件,因此,如果可能,應始終在類型別名上使用介面。

資源搜尋網站大全 https://www.renrenfan.com.cn 廣州VI設計公司https://www.houdianzi.com

更多關於介面和物件的知識點

函式是 JS 中的一等公民,而物件是該語言中最重要的實體。

物件大多是鍵/值對的容器,它們也可以儲存函式,這一點也不奇怪。當一個函式位於一個物件內部時,它可以通過關鍵字this訪問“宿主”物件:

const tom = {
  name: "web前端",
  city: "廈門",
  age: 26,
  printDetails: function() {
    console.log(`${this.name} - ${this.city}`);
  }
};

到目前為止,咱們已經看到 TypeScript 介面應用於簡單物件,用於描述字串和數字。 但是他們可以做的更多。 舉個例, 使用以下程式碼建立一個名為interfaces-functions.ts的新檔案:

const tom = {
  name: "web前端",
  city: "廈門",
  age: 26,
  printDetails: function() {
    console.log(`${this.name} - ${this.city}`);
  }
};

這是一個 JS 物件,咱們使用介面IPerson給它加上型別:

interface IPerson {
  name: string;
  city: string;
  age: number;
}

const tom: IPerson = {
  name: "web前端",
  city: "廈門",
  age: 26,
  printDetails: function() {
    console.log(`${this.name} - ${this.city}`);
  }
};

編譯程式碼並檢視報錯資訊:

interfaces-functions.ts:11:3 - error TS2322: Type '{ name: string; city: string; age: number; printDetails: () => void; }' is not assignable to type 'IPerson'.
  Object literal may only specify known properties, and 'printDetails' does not exist in type 'IPerson'.

IPerson沒有任何名為printDetails的屬性,但更重要的是它應該是一個函式。幸運的是,TypeScript 介面也可以描述函式。如下所示:

interface IPerson {
  name: string;
  city: string;
  age: number;
  printDetails(): void;
}

在這裡,我們添加了型別函式的屬性printDetails,返回void。void表示不返回任何值。

實際上,列印到控制檯的函式不會返回任何內容。 如果要從printDetails返回字串,則可以將返回型別調整為string:

interface IPerson {
  name: string;
  city: string;
  age: number;
  printDetails(): string;
}

const tom: IPerson = {
  name: "web前端",
  city: "廈門",
  age: 26,
  printDetails: function() {
    return `${this.name} - ${this.city}`;
  }
};

如果函式有引數呢? 在介面中,可以為它們新增型別註釋

interface IPerson {
  name: string;
  city: string;
  age: number;
  printDetails(): string;
  anotherFunc(a: number, b: number): number;
}

總結

這裡無法涵蓋每一個 TypeScript 特性。例如,省略[了ES2015類及其與介面或更高階型別]6的關係。當然後續會持續介紹。

在這個 TypeScript 教程中,講了:

  • 變數,函式引數和返回值的型別註釋
  • 介面
  • 自定義型別
  • 類型別名

    TS 幫助咱們減少一些 JS 程式碼隱藏的錯誤。需要重複的是,TypeScript 不能替代測試。 蛤它確實是一個有價值的工具,一開始很難掌握,但完全值得投資。