什么是 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 支持的代碼編輯器讀取。
- noImplicitAny true:當變量沒有定義類型時,TypeScript 會報錯
- alwaysStrict true:嚴格模式是 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.ts 中 filterByTerm 函數
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 用法最好事例。那么在 interface 和 type 之間使用哪個呢? 我更喜歡復雜對象的接口。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 不能替代測試。 蛤它確實是一個有價值的工具,一開始很難掌握,但完全值得投資。