日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

TypeScript 终极初学者指南

發(fā)布時(shí)間:2023/12/9 编程问答 52 豆豆
生活随笔 收集整理的這篇文章主要介紹了 TypeScript 终极初学者指南 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

大家好,我是若川。持續(xù)組織了8個(gè)月源碼共讀活動(dòng),感興趣的可以點(diǎn)此加我微信 ruochuan12?參與,每周大家一起學(xué)習(xí)200行左右的源碼,共同進(jìn)步。同時(shí)極力推薦訂閱我寫的《學(xué)習(xí)源碼整體架構(gòu)系列》?包含20余篇源碼文章。歷史面試系列

在過去的幾年里 TypeScript 變得越來越流行,現(xiàn)在許多工作都要求開發(fā)人員了解 TypeScript,各大廠的大型項(xiàng)目基本都要求使用 TypeScript 編寫。

如果你已經(jīng)對(duì) JavaScript 很熟了, TypeScript 基本上也能快速上手,下面是我整理的一些初學(xué)者必備的一些知識(shí)點(diǎn),如果你已經(jīng)是個(gè) TS 高手了,可以期待我后續(xù)的文章了~

Typescript 簡(jiǎn)介

據(jù)官方描述:TypeScript 是 JavaScript 的超集,這意味著它可以完成 JavaScript 所做的所有事情,而且額外附帶了一些能力。

JavaScript 本身是一種動(dòng)態(tài)類型語言,這意味著變量可以改變類型。使用 TypeScript 的主要原因是就是為了給 JavaScript 添加靜態(tài)類型。靜態(tài)類型意味著變量的類型在程序中的任何時(shí)候都不能改變。它可以防止很多bug !

Typescript 值得學(xué)嗎?

下面是學(xué)習(xí) Typescript 的幾個(gè)理由:

  • 研究表明,TypeScript 可以發(fā)現(xiàn) 15% 的常見 bug。

  • TypeScript 可以讓代碼的可讀性更好,你可以更好的理解代碼是在做什么。

  • TypeScript 可以你申請(qǐng)到更多好工作。

  • 學(xué)習(xí) TypeScript 可以使你對(duì) JavaScript 有更好的理解和新的視角。

當(dāng)然,使用 Typescript 也有一些缺點(diǎn):

  • TypeScript 的編寫時(shí)間比 JavaScript 要長(zhǎng),因?yàn)槟惚仨氁付愋?#xff0c;對(duì)于一些較小的獨(dú)立項(xiàng)目,可能不值使用。

  • TypeScript 需要編譯,項(xiàng)目越大消耗時(shí)間越長(zhǎng)。

但是,相比于提前發(fā)現(xiàn)更多的 bug,花更長(zhǎng)的時(shí)間也是值得的。

TypeScript 中的類型

原始類型

在 JavaScript 中,有 7 種原始類型:

  • string

  • number

  • bigint

  • boolean

  • undefined

  • null

  • symbol

原始類型都是不可變的,你可以為原始類型的變量重新分配一個(gè)新值,但不能像更改對(duì)象、數(shù)組和函數(shù)一樣更改它的值。可以看下面的例子:

let?name?=?'ConardLi'; name.toLowerCase(); console.log(name);?//?ConardLi?-?字符串的方法并沒有改變字符串本身let?arr?=?[1,?3,?5,?7]; arr.pop(); console.log(arr);?//?[1,?3,?5]?-?數(shù)組的方法改變了數(shù)組

回到 TypeScript ,我們可以在聲明一個(gè)變量之后設(shè)置我們想要添加的類型 :type (我們一般稱之為“類型注釋”或“類型簽名”):

let?id:?number?=?5; let?firstname:?string?=?'ConardLi'; let?hasDog:?boolean?=?true;let?unit:?number;?//?聲明變量而不賦值 unit?=?5;

但是,如果變量有默認(rèn)值的話,一般我們也不需要顯式聲明類型,TypeScript ?會(huì)自動(dòng)推斷變量的類型(類型推斷):

let?id?=?5;?//?number?類型 let?firstname?=?'ConardLi';?//?string?類型 let?hasDog?=?true;?//?boolean?類型hasDog?=?'yes';?//?ERROR

我們還可以將變量設(shè)置為聯(lián)合類型(聯(lián)合類型是可以分配多個(gè)類型的變量):

let?age:?string?|?number; age?=?17; age?=?'17';

TypeScript 中的數(shù)組

在 TypeScript 中,你可以定義數(shù)組包含的數(shù)據(jù)類型:

let?ids:?number[]?=?[1,?2,?3,?4,?5];?//?只能包含?number let?names:?string[]?=?['ConardLi',?'Tom',?'Jerry'];?//?只能包含?string let?options:?boolean[]?=?[true,?false,?false];?只能包含?true?false let?books:?object[]?=?[{?name:?'Tom',?animal:?'cat'?},{?name:?'Jerry',?animal:?'mouse'?}, ];?//?只能包含對(duì)象 let?arr:?any[]?=?['hello',?1,?true];?//?啥都行,回到了?JSids.push(6); ids.push('7');?//?ERROR:?Argument?of?type?'string'?is?not?assignable?to?parameter?of?type?'number'.

你也可以使用聯(lián)合類型來定義包含多種類型的數(shù)組:

let?person:?(string?|?number?|?boolean)[]?=?['ConardLi',?1,?true]; person[0]?=?100; person[1]?=?{name:?'ConardLi'}?//?Error?-?person?array?can't?contain?objects

如果數(shù)組有默認(rèn)值, TypeScript 同樣也會(huì)進(jìn)行類型推斷:

let?person?=?['ConardLi',?1,?true];?//?和上面的例子一樣 person[0]?=?100; person[1]?=?{?name:?'ConardLi'?};?//?Error?-?person?array?can't?contain?objects

TypeScript 中可以定義一種特殊類型的數(shù)組:元組(Tuple)。元組是具有固定大小和已知數(shù)據(jù)類型的數(shù)組,它比常規(guī)數(shù)組更嚴(yán)格。

let?person:?[string,?number,?boolean]?=?['ConardLi',?1,?true]; person[0]?=?17;?//?Error?-?Value?at?index?0?can?only?be?a?string

TypeScript 中的對(duì)象

TypeScript 中的對(duì)象必須擁有所有正確的屬性和值類型:

//?使用特定的對(duì)象類型注釋聲明一個(gè)名為?person?的變量 let?person:?{name:?string;age:?number;isProgrammer:?boolean; };//?給?person?分配一個(gè)具有所有必要屬性和值類型的對(duì)象 person?=?{name:?'ConardLi',age:?17,isProgrammer:?true, };person.age?=?'17';?//?ERROR:?should?be?a?numberperson?=?{name:?'Tom',age:?3, };? //?ERROR:?missing?the?isProgrammer?property

在定義對(duì)象的類型時(shí),我們通常會(huì)使用 interface。如果我們需要檢查多個(gè)對(duì)象是否具有相同的特定屬性和值類型時(shí),是很有用的:

interface?Person?{name:?string;age:?number;isProgrammer:?boolean; }let?person1:?Person?=?{name:?'ConardLi',age:?17,isProgrammer:?true, };let?person2:?Person?=?{name:?'Tom',age:?3,isProgrammer:?false, };

我們還可以用函數(shù)的類型簽名聲明一個(gè)函數(shù)屬性,通用函數(shù)(sayHi)和箭頭函數(shù)(sayBye)都可以聲明:

interface?Animal?{eat(name:?string):?string;speak:?(name:?string)?=>?string; }let?tom:?Animal?=?{eat:?function?(name:?string)?{return?`eat?${name}`;},speak:?(name:?string)?=>?`speak?${name}`, };console.log(tom.eat('Jerry')); console.log(tom.speak('哈哈哈'));

需要注意的是,雖然 eat、speak 分別是用普通函數(shù)和箭頭函數(shù)聲明的,但是它們具體是什么樣的函數(shù)類型都可以,Typescript 是不關(guān)心這些的。

TypeScript 中的函數(shù)

我們可以定義函數(shù)參數(shù)和返回值的類型:

//?定義一個(gè)名為?circle?的函數(shù),它接受一個(gè)類型為?number?的直徑變量,并返回一個(gè)字符串 function?circle(diam:?number):?string?{return?'圓的周長(zhǎng)為:'?+?Math.PI?*?diam; }console.log(circle(10));?//?圓的周長(zhǎng)為:31.41592653589793

ES6 箭頭函數(shù)的寫法:

const?circle?=?(diam:?number):?string?=>?{return?'圓的周長(zhǎng)為:'?+?Math.PI?*?diam; };

我們沒必要明確聲明 circle 是一個(gè)函數(shù),TypeScript 會(huì)進(jìn)行類型推斷。TypeScript 還會(huì)推斷函數(shù)的返回類型,但是如果函數(shù)體比較復(fù)雜,還是建議清晰的顯式聲明返回類型。

我們可以在參數(shù)后添加一個(gè)?,表示它為可選參數(shù);另外參數(shù)的類型也可以是一個(gè)聯(lián)合類型:

const?add?=?(a:?number,?b:?number,?c?:?number?|?string)?=>?{console.log(c);return?a?+?b; };console.log(add(5,?4,?'可以是?number、string,也可以為空'));

如果函數(shù)沒有返回值,在 TS 里表示為返回 void,你也不需要顯式聲明,TS 一樣可以進(jìn)行類型推斷:

const?log?=?(msg:?string):?void?=>?{console.log('打印一些內(nèi)容:?'?+?msg); };

any 類型

使 any 類型,我們基本上可以將 TypeScript 恢復(fù)為 JavaScript:

let?name:?any?=?'ConardLi'; name?=?17; name?=?{?age:?17?};

如果代碼里使用了大量的 any,那 TypeScript 也就失去了意義,所以我們應(yīng)該盡量避免使用 any 。

DOM 和類型轉(zhuǎn)換

TypeScript 沒辦法像 JavaScript 那樣訪問 DOM。這意味著每當(dāng)我們嘗試訪問 DOM 元素時(shí),TypeScript 都無法確定它們是否真的存在。

const?link?=?document.querySelector('a');console.log(link.href);?//?ERROR:?Object?is?possibly?'null'.?TypeScript?can't?be?sure?the?anchor?tag?exists,?as?it?can't?access?the?DOM

使用非空斷言運(yùn)算符 (!),我們可以明確地告訴編譯器一個(gè)表達(dá)式的值不是 null 或 undefined。當(dāng)編譯器無法準(zhǔn)確地進(jìn)行類型推斷時(shí),這可能很有用:

//?我們明確告訴?TS?a?標(biāo)簽肯定存在 const?link?=?document.querySelector('a')!;console.log(link.href);?//?conardli.top

這里我們沒必要聲明 link 變量的類型。這是因?yàn)?TypeScript 可以通過類型推斷確認(rèn)它的類型為 HTMLAnchorElement。

但是如果我們需要通過 class 或 id 來選擇一個(gè) DOM 元素呢?這時(shí) TypeScript 就沒辦法推斷類型了:

const?form?=?document.getElementById('signup-form');console.log(form.method); //?ERROR:?Object?is?possibly?'null'. //?ERROR:?Property?'method'?does?not?exist?on?type?'HTMLElement'.

我們需要告訴 TypeScript form 確定是存在的,并且我們知道它的類型是 ?HTMLFormElement。我們可以通過類型轉(zhuǎn)換來做到這一點(diǎn):

const?form?=?document.getElementById('signup-form')?as?HTMLFormElement;console.log(form.method);?//?post

TypeScript 還內(nèi)置了一個(gè) Event 對(duì)象。如果我們?cè)诒韱沃刑砑右粋€(gè) submit 的事件偵聽器,TypeScript 可以自動(dòng)幫我們推斷類型錯(cuò)誤:

const?form?=?document.getElementById('signup-form')?as?HTMLFormElement;form.addEventListener('submit',?(e:?Event)?=>?{e.preventDefault();?//?阻止頁面刷新console.log(e.tarrget);?//?ERROR:?Property?'tarrget'?does?not?exist?on?type?'Event'.?Did?you?mean?'target'? });

TypeScript 中的類

我們可以定義類中每條數(shù)據(jù)的類型:

class?Person?{name:?string;isCool:?boolean;age:?number;constructor(n:?string,?c:?boolean,?a:?number)?{this.name?=?n;this.isCool?=?c;this.age?=?a;}sayHello()?{return?`Hi,我是?${this.name}?,我今年?${this.age}?歲了`;} }const?person1?=?new?Person('ConardLi',?true,?17); const?person2?=?new?Person('Jerry',?'yes',?20);?//?ERROR:?Argument?of?type?'string'?is?not?assignable?to?parameter?of?type?'boolean'.console.log(person1.sayHello());?//?Hi,?我是?ConardLi,我今年?17?歲了

我們可以創(chuàng)建一個(gè)僅包含從 Person 構(gòu)造的對(duì)象數(shù)組:

let?People:?Person[]?=?[person1,?person2];

我們可以給類的屬性添加訪問修飾符,TypeScript 還提供了一個(gè)新的 readonly 訪問修飾符。

class?Person?{readonly?name:?string;?//?不可以變的private?isCool:?boolean;?//?類的私有屬性、外部訪問不到protected?email:?string;?//?只能從這個(gè)類和子類中進(jìn)行訪問和修改public?age:?number;?//?任何地方都可以訪問和修改constructor(n:?string,?c:?boolean,?a:?number)?{this.name?=?n;this.isCool?=?c;this.age?=?a;}sayHello()?{return?`Hi,我是?${this.name}?,我今年?${this.age}?歲了`;} }const?person1?=?new?Person('ConardLi',?true,?'conard@xx.com',?17); console.log(person1.name);?//?ConardLi person1.name?=?'Jerry';?//?Error:?read?only

我們可以通過下面的寫法,屬性會(huì)在構(gòu)造函數(shù)中自動(dòng)分配,我們類會(huì)更加簡(jiǎn)潔:

class?Person?{constructor(readonly?name:?string,private?isCool:?boolean,protected?email:?string,public?age:?number)?{} }

如果我們省略訪問修飾符,默認(rèn)情況下屬性都是 public,另外和 JavaScript 一樣,類也是可以 extends 的。

TypeScript 中的接口

接口定義了對(duì)象的外觀:

interface?Person?{name:?string;age:?number; }function?sayHi(person:?Person)?{console.log(`Hi?${person.name}`); }sayHi({name:?'ConardLi',age:?17, });?//?Hi?ConardLi

你還可以使用類型別名定義對(duì)象類型:

type?Person?=?{name:?string;age:?number; };

或者可以直接匿名定義對(duì)象類型:

function?sayHi(person:?{?name:?string;?age:?number?})?{console.log(`Hi?${person.name}`); }

interface 和 type 非常相似,很多情況下它倆可以隨便用。比如它們兩個(gè)都可以擴(kuò)展:

擴(kuò)展 interface:

interface?Animal?{name:?string }interface?Bear?extends?Animal?{honey:?boolean }const?bear:?Bear?=?{name:?"Winnie",honey:?true, }

擴(kuò)展 type:

type?Animal?=?{name:?string }type?Bear?=?Animal?&?{honey:?boolean }const?bear:?Bear?=?{name:?"Winnie",honey:?true, }

但是有個(gè)比較明顯的區(qū)別,interface 是可以自動(dòng)合并類型的,但是 type 不支持:

interface?Animal?{name:?string }interface?Animal?{tail:?boolean }const?dog:?Animal?=?{name:?"Tom",tail:?true, }

類型別名在創(chuàng)建后無法更改:

type?Animal?=?{name:?string }type?Animal?=?{tail:?boolean } //?ERROR:?Duplicate?identifier?'Animal'.

一般來說,當(dāng)你不知道用啥的時(shí)候,默認(rèn)就用 interface 就行,直到 interface 滿足不了我們的需求的時(shí)候再用 type。

類的 interface

我們可以通過實(shí)現(xiàn)一個(gè)接口來告訴一個(gè)類它必須包含某些屬性和方法:

interface?HasFormatter?{format():?string; }class?Person?implements?HasFormatter?{constructor(public?username:?string,?protected?password:?string)?{}format()?{return?this.username.toLocaleLowerCase();} }let?person1:?HasFormatter; let?person2:?HasFormatter;person1?=?new?Person('ConardLi',?'admin123'); person2?=?new?Person('Tom',?'admin123');console.log(person1.format());?//?conardli

確保 people 是一個(gè)實(shí)現(xiàn) HasFormatter 的對(duì)象數(shù)組(確保每 people 都有 format 方法):

let?people:?HasFormatter[]?=?[]; people.push(person1); people.push(person2);

泛型

泛型可以讓我們創(chuàng)建一個(gè)可以在多種類型上工作的組件,它能夠支持當(dāng)前的數(shù)據(jù)類型,同時(shí)也能支持未來的數(shù)據(jù)類型,這大大提升了組件的可重用性。我們來看下面這個(gè)例子:

addID 函數(shù)接受一個(gè)任意對(duì)象,并返回一個(gè)新對(duì)象,其中包含傳入對(duì)象的所有屬性和值,以及一個(gè) 0 到 1000 之間隨機(jī)的 id 屬性。

const?addID?=?(obj:?object)?=>?{let?id?=?Math.floor(Math.random()?*?1000);return?{?...obj,?id?}; };let?person1?=?addID({?name:?'John',?age:?40?});console.log(person1.id);?//?271 console.log(person1.name);?//?ERROR:?Property?'name'?does?not?exist?on?type?'{?id:?number;?}'.

當(dāng)我們嘗試訪問 name 屬性時(shí),TypeScript 會(huì)出錯(cuò)。這是因?yàn)楫?dāng)我們將一個(gè)對(duì)象傳遞給 addID 時(shí),我們并沒有指定這個(gè)對(duì)象應(yīng)該有什么屬性 —— 所以 TypeScript 不知道這個(gè)對(duì)象有什么屬性。因此,TypeScript 知道的唯一屬性返回對(duì)象的 id。

那么,我們?cè)趺磳⑷我鈱?duì)象傳遞給 addID,而且仍然可以告訴 TypeScript 該對(duì)象具有哪些屬性和值?這種場(chǎng)景就可以使用泛型了, <T> – T 被稱為類型參數(shù):

//?<T>?只是一種編寫習(xí)慣?-?我們也可以用?<X>?或?<A> const?addID?=?<T>(obj:?T)?=>?{let?id?=?Math.floor(Math.random()?*?1000);return?{?...obj,?id?}; };

這是啥意思呢?現(xiàn)在當(dāng)我們?cè)賹⒁粋€(gè)對(duì)象傳遞給 addID 時(shí),我們已經(jīng)告訴 TypeScript 來捕獲它的類型了 —— 所以 T 就變成了我們傳入的任何類型。addID 現(xiàn)在會(huì)知道我們傳入的對(duì)象上有哪些屬性。

但是,現(xiàn)在有另一個(gè)問題:任何東西都可以傳入 addID ,TypeScript 將捕獲類型而且并不會(huì)報(bào)告問題:

let?person1?=?addID({?name:?'ConardLi',?age:?17?}); let?person2?=?addID('Jerry');?//?傳遞字符串也沒問題console.log(person1.id);?//?188 console.log(person1.name);?//?ConardLiconsole.log(person2.id); console.log(person2.name);?//?ERROR:?Property?'name'?does?not?exist?on?type?'"Jerry"?&?{?id:?number;?}'.

當(dāng)我們傳入一個(gè)字符串時(shí),TypeScript 沒有發(fā)現(xiàn)任何問題。只有我們嘗試訪問 name 屬性時(shí)才會(huì)報(bào)告錯(cuò)誤。所以,我們需要一個(gè)約束:我們需要通過將泛型類型 T 作為 object 的擴(kuò)展,來告訴 TypeScript 只能接受對(duì)象:

const?addID?=?<T?extends?object>(obj:?T)?=>?{let?id?=?Math.floor(Math.random()?*?1000);return?{?...obj,?id?}; };let?person1?=?addID({?name:?'John',?age:?40?}); let?person2?=?addID('Jerry');?//?ERROR:?Argument?of?type?'string'?is?not?assignable?to?parameter?of?type?'object'.

錯(cuò)誤馬上就被捕獲了,完美…… 好吧,也不完全是。在 JavaScript 中,數(shù)組也是對(duì)象,所以我們?nèi)匀豢梢酝ㄟ^傳入數(shù)組來逃避類型檢查:

let?person2?=?addID(['ConardLi',?17]);?//?傳遞數(shù)組沒問題console.log(person2.id);?//?188 console.log(person2.name);?//?Error:?Property?'name'?does?not?exist?on?type?'(string?|?number)[]?&?{?id:?number;?}'.

要解決這個(gè)問題,我們可以這樣說:object 參數(shù)應(yīng)該有一個(gè)帶有字符串值的 name 屬性:

const?addID?=?<T?extends?{?name:?string?}>(obj:?T)?=>?{let?id?=?Math.floor(Math.random()?*?1000);return?{?...obj,?id?}; };let?person2?=?addID(['ConardLi',?17]);?//?ERROR:?argument?should?have?a?name?property?with?string?value

泛型允許在參數(shù)和返回類型提前未知的組件中具有類型安全。

在 TypeScript 中,泛型用于描述兩個(gè)值之間的對(duì)應(yīng)關(guān)系。在上面的例子中,返回類型與輸入類型有關(guān)。我們用一個(gè)泛型來描述對(duì)應(yīng)關(guān)系。

另一個(gè)例子:如果需要接受多個(gè)類型的函數(shù),最好使用泛型而不是 any 。下面展示了使用 any 的問題:

function?logLength(a:?any)?{console.log(a.length);?//?No?errorreturn?a; }let?hello?=?'Hello?world'; logLength(hello);?//?11let?howMany?=?8; logLength(howMany);?//?undefined?(but?no?TypeScript?error?-?surely?we?want?TypeScript?to?tell?us?we've?tried?to?access?a?length?property?on?a?number!)

我們可以嘗試使用泛型:

function?logLength<T>(a:?T)?{console.log(a.length);?//?ERROR:?TypeScript?isn't?certain?that?`a`?is?a?value?with?a?length?propertyreturn?a; }

好,至少我們現(xiàn)在得到了一些反饋,可以幫助我們持續(xù)改進(jìn)我們的代碼。

解決方案:使用一個(gè)泛型來擴(kuò)展一個(gè)接口,確保傳入的每個(gè)參數(shù)都有一個(gè) length 屬性:

interface?hasLength?{length:?number; }function?logLength<T?extends?hasLength>(a:?T)?{console.log(a.length);return?a; }let?hello?=?'Hello?world'; logLength(hello);?//?11let?howMany?=?8; logLength(howMany);?//?Error:?numbers?don't?have?length?properties

我們也可以編寫這樣一個(gè)函數(shù),它的參數(shù)是一個(gè)元素?cái)?shù)組,這些元素都有一個(gè) length 屬性:

interface?hasLength?{length:?number; }function?logLengths<T?extends?hasLength>(a:?T[])?{a.forEach((element)?=>?{console.log(element.length);}); }let?arr?=?['This?string?has?a?length?prop',['This',?'arr',?'has',?'length'],{?material:?'plastic',?length:?17?}, ];logLengths(arr); //?29 //?4 //?30

泛型是 TypeScript 的一個(gè)很棒的特性!

泛型接口

當(dāng)我們不知道對(duì)象中的某個(gè)值是什么類型時(shí),可以使用泛型來傳遞該類型:

//?The?type,?T,?will?be?passed?in interface?Person<T>?{name:?string;age:?number;documents:?T; }//?We?have?to?pass?in?the?type?of?`documents`?-?an?array?of?strings?in?this?case const?person1:?Person<string[]>?=?{name:?'ConardLi',age:?17,documents:?['passport',?'bank?statement',?'visa'], };//?Again,?we?implement?the?`Person`?interface,?and?pass?in?the?type?for?documents?-?in?this?case?a?string const?person2:?Person<string>?=?{name:?'Tom',age:?20,documents:?'passport,?P45', };

枚舉

枚舉是 TypeScript 給 JavaScript 帶來的一個(gè)特殊特性。枚舉允許我們定義或聲明一組相關(guān)值,可以是數(shù)字或字符串,作為一組命名常量。

enum?ResourceType?{BOOK,AUTHOR,FILM,DIRECTOR,PERSON, }console.log(ResourceType.BOOK);?//?0 console.log(ResourceType.AUTHOR);?//?1//?從?1?開始 enum?ResourceType?{BOOK?=?1,AUTHOR,FILM,DIRECTOR,PERSON, }console.log(ResourceType.BOOK);?//?1 console.log(ResourceType.AUTHOR);?//?2

默認(rèn)情況下,枚舉是基于數(shù)字的 — 它們將字符串值存儲(chǔ)為數(shù)字。但它們也可以是字符串:

enum?Direction?{Up?=?'Up',Right?=?'Right',Down?=?'Down',Left?=?'Left', }console.log(Direction.Right);?//?Right console.log(Direction.Down);?//?Down

當(dāng)我們有一組相關(guān)的常量時(shí),枚舉就可以派上用場(chǎng)了。例如,與在代碼中使用非描述性數(shù)字不同,枚舉通過描述性常量使代碼更具可讀性。

枚舉還可以防止錯(cuò)誤,因?yàn)楫?dāng)你輸入枚舉的名稱時(shí),智能提示將彈出可能選擇的選項(xiàng)列表。

TypeScript 嚴(yán)格模式

建議在 tsconfig.json 中啟用所有嚴(yán)格的類型檢查操作文件。這可能會(huì)導(dǎo)致 TypeScript 報(bào)告更多的錯(cuò)誤,但也更有助于幫你提前發(fā)現(xiàn)發(fā)現(xiàn)程序中更多的 bug。

//?tsconfig.json"strict":?true

嚴(yán)格模式實(shí)際上就意味著:禁止隱式 any 和 嚴(yán)格的空檢查。

禁止隱式 any

在下面的函數(shù)中,TypeScript 已經(jīng)推斷出參數(shù) a 是 any 類型的。當(dāng)我們向該函數(shù)傳遞一個(gè)數(shù)字,并嘗試打印一個(gè) name 屬性時(shí),沒有報(bào)錯(cuò):

function?logName(a)?{//?No?error??console.log(a.name); }logName(97);

打開 noImplicitAny 選項(xiàng)后,如果我們沒有顯式地聲明 a 的類型,TypeScript 將立即標(biāo)記一個(gè)錯(cuò)誤:

//?ERROR:?Parameter?'a'?implicitly?has?an?'any'?type. function?logName(a)?{console.log(a.name); }

嚴(yán)格的空檢查

當(dāng) strictNullChecks 選項(xiàng)為 false 時(shí),TypeScript 實(shí)際上會(huì)忽略 null 和 undefined。這可能會(huì)在運(yùn)行時(shí)導(dǎo)致意外錯(cuò)誤。

當(dāng) strictNullChecks 設(shè)置為 true 時(shí),null 和 undefined 有它們自己的類型,如果你將它們分配給一個(gè)期望具體值(例如,字符串)的變量,則會(huì)得到一個(gè)類型錯(cuò)誤。

let?whoSangThis:?string?=?getSong();const?singles?=?[{?song:?'touch?of?grey',?artist:?'grateful?dead'?},{?song:?'paint?it?black',?artist:?'rolling?stones'?}, ];const?single?=?singles.find((s)?=>?s.song?===?whoSangThis);console.log(single.artist);

singles.find 并不能保證它一定能找到這首歌 — 但是我們已經(jīng)編寫了下面的代碼,好像它肯定能找到一樣。

通過將 strictNullChecks 設(shè)置為 true, TypeScript 將拋出一個(gè)錯(cuò)誤,因?yàn)樵趪L試使用它之前,我們沒有保證 single 一定存在:

const?getSong?=?()?=>?{return?'song'; };let?whoSangThis:?string?=?getSong();const?singles?=?[{?song:?'touch?of?grey',?artist:?'grateful?dead'?},{?song:?'paint?it?black',?artist:?'rolling?stones'?}, ];const?single?=?singles.find((s)?=>?s.song?===?whoSangThis);console.log(single.artist);?//?ERROR:?Object?is?possibly?'undefined'.

TypeScript 基本上是告訴我們?cè)谑褂?single 之前要確保它存在。我們需要先檢查它是否為 null 或 undefined:

if?(single)?{console.log(single.artist);?//?rolling?stones }

TypeScript 中的類型收窄

在 TypeScript 中,變量可以從不太精確的類型轉(zhuǎn)移到更精確的類型,這個(gè)過程稱為類型收窄。

下面是一個(gè)簡(jiǎn)單的例子,展示了當(dāng)我們使用帶有 typeof 的 if 語句時(shí),TypeScript 如何將不太特定的 string | number 縮小到更特定的類型:

function?addAnother(val:?string?|?number)?{if?(typeof?val?===?'string')?{//?ts?將?val?視為一個(gè)字符串return?val.concat('?'?+?val);}//?ts?知道?val?在這里是一個(gè)數(shù)字return?val?+?val; }console.log(addAnother('哈哈'));?//?哈哈?哈哈 console.log(addAnother(17));?//?34

另一個(gè)例子:下面,我們定義了一個(gè)名為 allVehicles 的聯(lián)合類型,它可以是 Plane 或 Train 類型。

interface?Vehicle?{topSpeed:?number; }interface?Train?extends?Vehicle?{carriages:?number; }interface?Plane?extends?Vehicle?{wingSpan:?number; }type?PlaneOrTrain?=?Plane?|?Train;function?getSpeedRatio(v:?PlaneOrTrain)?{console.log(v.carriages);?//?ERROR:?'carriages'?doesn't?exist?on?type?'Plane' }

由于 getSpeedRatio 函數(shù)處理了多種類型,我們需要一種方法來區(qū)分 v 是 Plane 還是 Train 。我們可以通過給這兩種類型一個(gè)共同的區(qū)別屬性來做到這一點(diǎn),它帶有一個(gè)字符串值:

interface?Train?extends?Vehicle?{type:?'Train';carriages:?number; }interface?Plane?extends?Vehicle?{type:?'Plane';wingSpan:?number; }type?PlaneOrTrain?=?Plane?|?Train;

現(xiàn)在,TypeScript 可以縮小 v 的類型:

function?getSpeedRatio(v:?PlaneOrTrain)?{if?(v.type?===?'Train')?{return?v.topSpeed?/?v.carriages;}//?如果不是 Train,ts 知道它就是 Plane 了,聰明!return?v.topSpeed?/?v.wingSpan; }let?bigTrain:?Train?=?{type:?'Train',topSpeed:?100,carriages:?20, };console.log(getSpeedRatio(bigTrain));?//?5

另外,我們還可以通過實(shí)現(xiàn)一個(gè)類型保護(hù)來解決這個(gè)問題,可以看看這篇文章:什么是鴨子🦆類型?

TypeScript & React

TypeScript 完全支持 React 和 JSX。這意味著我們可以將 TypeScript 與三個(gè)最常見的 React 框架一起使用:

  • create-react-app (https://create-react-app.dev/docs/adding-typescript/)

  • Gatsby (https://www.gatsbyjs.com/docs/how-to/custom-configuration/typescript/)

  • Next.js (https://nextjs.org/learn/excel/typescript)

如果你需要一個(gè)更自定義的 React-TypeScript 配置,你可以字節(jié)配置 Webpack 和 tsconfig.json。但是大多數(shù)情況下,一個(gè)框架就可以完成這項(xiàng)工作。

例如,要用 TypeScript 設(shè)置 create-react-app,只需運(yùn)行:

npx?create-react-app?my-app?--template?typescript#?oryarn?create?react-app?my-app?--template?typescript

在 src 文件夾中,我們現(xiàn)在可以創(chuàng)建帶有 .ts (普通 TypeScript 文件)或 .tsx (帶有 React 的 TypeScript 文件)擴(kuò)展名的文件,并使用 TypeScript 編寫我們的組件。然后將其編譯成 public 文件夾中的 JavaScript 。

React props & TypeScript

Person 是一個(gè) React 組件,它接受一個(gè) props 對(duì)象,其中 name 應(yīng)該是一個(gè)字符串,age 是一個(gè)數(shù)字。

//?src/components/Person.tsx import?React?from?'react';const?Person:?React.FC<{name:?string;age:?number; }>?=?({?name,?age?})?=>?{return?(<div><div>{name}</div><div>{age}</div></div>); };export?default?Person;

一般我們更喜歡用 interface 定義 props:

interface?Props?{name:?string;age:?number; }const?Person:?React.FC<Props>?=?({?name,?age?})?=>?{return?(<div><div>{name}</div><div>{age}</div></div>); };

然后我們嘗試將組件導(dǎo)入到 App.tsx,如果我們沒有提供必要的 props,TypeScript 會(huì)報(bào)錯(cuò)。

import?React?from?'react'; import?Person?from?'./components/Person';const?App:?React.FC?=?()?=>?{return?(<div><Person?name='ConardLi'?age={17}?/></div>); };export?default?App;

React hooks & TypeScript

useState()

我們可以用尖括號(hào)來聲明狀態(tài)變量的類型。如果我們省略了尖括號(hào),TypeScript 會(huì)默認(rèn)推斷 cash 是一個(gè)數(shù)字。因此,如果想讓它也為空,我們必須指定:

const?Person:?React.FC<Props>?=?({?name,?age?})?=>?{const?[cash,?setCash]?=?useState<number?|?null>(1);setCash(null);return?(<div><div>{name}</div><div>{age}</div></div>); };

useRef()

useRef 返回一個(gè)可變對(duì)象,該對(duì)象在組件的生命周期內(nèi)都是持久的。我們可以告訴 TypeScript ? ref 對(duì)象應(yīng)該指向什么:

const?Person:?React.FC?=?()?=>?{//?Initialise?.current?property?to?nullconst?inputRef?=?useRef<HTMLInputElement>(null);return?(<div><input?type='text'?ref={inputRef}?/></div>); };

參考

  • https://www.typescriptlang.org/docs/

  • https://react-typescript-cheatsheet.netlify.app/

  • https://www.freecodecamp.org/news/learn-typescript-beginners-guide

好了,這篇文章我們學(xué)習(xí)了一些 Typescript 的必備基礎(chǔ),有了這些知識(shí)你已經(jīng)可以應(yīng)付大部分 TS 的應(yīng)用場(chǎng)景了,后續(xù)我會(huì)出一些 TS 的高級(jí)技巧相關(guān)的文章,敬請(qǐng)期待吧 ~

·················?若川簡(jiǎn)介?·················

你好,我是若川,畢業(yè)于江西高校。現(xiàn)在是一名前端開發(fā)“工程師”。寫有《學(xué)習(xí)源碼整體架構(gòu)系列》20余篇,在知乎、掘金收獲超百萬閱讀。
從2014年起,每年都會(huì)寫一篇年度總結(jié),已經(jīng)堅(jiān)持寫了8年,點(diǎn)擊查看年度總結(jié)。
同時(shí),最近組織了源碼共讀活動(dòng),幫助3000+前端人學(xué)會(huì)看源碼。公眾號(hào)愿景:幫助5年內(nèi)前端人走向前列。

掃碼加我微信 ruochuan02、拉你進(jìn)源碼共讀

今日話題

目前建有江西|湖南|湖北?籍 前端群,想進(jìn)群的可以加我微信 ruochuan12?進(jìn)群分享、收藏、點(diǎn)贊、在看我的文章就是對(duì)我最大的支持~

總結(jié)

以上是生活随笔為你收集整理的TypeScript 终极初学者指南的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。