TypeScript免费视频图文教程(2W字)
00. B站視頻觀看地址
出這個教程也是為了迎接Vue3的推出,今晚更新最后一集視頻后,開始更新Vue3視頻。
01.TypeScript 簡介和課程介紹
我原本準備更新 Vue3.x 教程的,由于官方文檔一直不出,我又不敢瞎講,所以干脆先來一個 TypeScript 教程熱身,TypeScript 已經在我們公司全面使用了,現在招聘的環節也增加了一個要求,就是要求必須熟練使用 TypeScript。我以前也出過 TypeScript 的教程,不過那個太簡單,不夠深入。所以在這個空檔期,準備自己重學一下 TypeScript,然后也也出一個比較完全的教程。
努力做到全網最好的免費 TypeScript 圖文視頻教程。
這里我建議你耐心點,跟我一起學習這版最新的《TypeScript 從入門到精通圖文視頻教程》,這樣能更好的對 TypeScript 有全面的了解。
TypeScript 簡介
TypeScript 是由微軟公司在 2012 年正式發布,現在也有 8 年的不斷更新和維護了,TypeScript 的成長速度是非常快的,現在已經變成了前端必會的一門技能。TypeScript 其實就是 JavaScript 的超集,也就是說 TypeScript 是建立在 JavaScript 之上的,最后都會轉變成 JavaScript。這就好比漫威里的鋼鐵俠,沒穿裝甲之前,他實力一般,雖然聰明有錢,但還是接近人類。但是有了裝甲,他就厲害太多了,甚至可以和神干一架。
也許你會覺的我這說的太夸張,我剛開始學習時,也是這樣想的,但是用了 2 年左右,特別是大型項目中,真的好用。比如說它的擴展語法、面向對象、靜態類型。如果一個前端項目我可以做主,我會在強烈的要求所有人都使用 TypeScript 的語法進行編程。
使用 VSCode 進行編寫代碼
我的所有前端課程都是使用 VSCode 進行編寫代碼的,因為這是我在工作中用的最多的編輯器,也是目前我認為最好用的編輯器。
下載地址:code.visualstudio.com/
如果你已經參加了工作,我相信你身邊至少有 80%以上的前端,在使用這款編輯器。
使用 VSCode 講解,還有一個主要的愿意就是他們都是微軟出的產品,所以有很好的兼容性和支持,VSCode 上有很多專門為 TypeScript 專門作的語法適配,所以在編寫時就會輕松快樂起來。
TypeScript 開發環境搭建
如果你想使用 TypeScript 來編寫代碼,你需要先安裝一下它的開發環境,這并不復雜。
1.安裝 Node 的運行環境
你可以到Node.js官網去下載 Node 進行安裝(node.js.org),建議你下載LTS版本,也就是長期支持版本。安裝的過程我就不演示了,這個過程就和安裝 QQ 一樣,沒有任何難度。
如果你已經安裝了,可以打開命令行工具,然后使用node -v命令查看安裝的版本,但是一般還有一個命令需要檢測一下,就是npm -v,如果兩個命令都可以輸出版本號,說明你的 Node 安裝已經沒有任何問題了。
2.全局安裝 typeScript
你要使用 TypeScript 先要在你的系統中全局安裝一下TypeScript,這里你可以直接在 VSCode 中進行安裝,安裝命令可以使用 npm 也可以使用 yarn
npm install typescript -g 復制代碼 yarn global add typescript 復制代碼這兩個你使用那個都是可以的,根據喜好自行選擇,我在視頻中使用的npm進行安裝。
3.建立項目目錄和編譯 TS 文件
在E盤(當然你可以在你喜歡的任何一個地方安裝),新建一個目錄,我這里起的目錄名字叫做TypeScriptDemo,然后把這個文件在 VSCode 中打開。 我在視頻里用了命令行的形式建立,直接使用ctrl+r打開運行,然后在運行的文本框里輸入cmd,回車后,打開命令行工具,在命令行中輸入下面的命令。
e: mkdir TypeScriptDemo 復制代碼完成后,打開 E 盤,打開 VSCode,把新建立的文件夾拖入到 VSCode 當中,新建一個Demo1.ts文件,寫入下面代碼:
function jspang() {let web: string = "Hello World";console.log(web); }jspang(); 復制代碼這時候你使用node Demo1.ts是執行不成功的,因為 Node 不能直接運行TypeScript文件,需要用tsc Demo1.ts轉換一下,轉換完成后typeScript代碼被編譯成了javaScript代碼,新生成一個demo1.js的文件,這時候你在命令行輸入node Demo1.js,在終端里就可以順利的輸出jspang的字符了。
4.ts-node 的安裝和使用
但是這樣操作的效率實在是太低了,你可以使用ts-node插件來解決這個問題,有了這個插件,我們就不用再編譯了,而使用ts-node就可以直接看到編寫結果。
使用npm命令來全局安裝,直接在命令行輸入下面的命令:
npm install -g ts-node 復制代碼安裝完成后,就可以在命令中直接輸入如下命令,來查看結果了。
ts-node Demo1.ts 復制代碼好了,今天就學這么多吧,恭喜你有學會了新的編程技能,小伙伴們,加油!!!
02.TypeScript 的靜態類型
TypeScript 的一個最主要特點就是可以定義靜態類型,英文是 Static Typing。那到底是什么意思那?太復雜的概念性東西這里就不講了,你可以簡單的理解“靜態類型”為,就是你一旦定義了,就不可以再改變了。比如你是男人就是男人,一輩子都要作男人;是女人就是女人,一輩子都是女人。這個事不可以改變!呃…好像現在也可以隨便變來變去啊,這里說的是正常情況。但是它還有一些特性,這個并不像表面的那么簡單。現在我們就來學習。
如何定義靜態類型
你可以在上節課的文件夾下建立一個新的demo2.ts文件,然后寫下這段代碼:
const count: number = 1; 復制代碼這就是最簡單的定義一個數字類型的count的變量,這里的: number就是定義了一個靜態類型。這樣定義后count這個變量在程序中就永遠都是數字類型了,不可以改變了。比如我們這時候給count復制一個字符串,它就報錯了。
//錯誤代碼 const count: number = 1; count = "jspang"; 復制代碼但這只是最簡單的理解,再往深一層次理解,你會發現這時候的count變量,可以使用number類型上所有的屬性和方法。我們可以通過在count后邊打上一個.看出這個特性,并且編輯器會給你非常好的提示。這也是為什么我喜歡用VScode編輯器的一個原因。
自定義靜態類型
你還可以自己去定義一個靜態類型,比如現在你定義一個小姐姐的類型,然后在聲明變量的時候,就可以使用這個靜態類型了,看下面的代碼。
interface XiaoJieJie {uname: string;age: number; }const xiaohong: XiaoJieJie = {uname: "小紅",age: 18, }; 復制代碼這時候你如果聲明變量,跟自定義不一樣,VSCode直接就會報錯。需要注意的是,這時候xiaohong變量也具有uname和age屬性了。
這節課你需要記住的是,如果使用了靜態類型,不僅意味著變量的類型不可以改變,還意味著類型的屬性和方法也跟著確定了。這個特點就大大提高了程序的健壯性,并且編輯器這時候也會給你很好的語法提示,加快了你的開發效率。
隨著你不斷的深入學習,你會對這兩個優點有更深的理解。
03.TypeScript 基礎靜態類型和對象類型
在 TypeScript 靜態類型分為兩種,一種是基礎靜態類型,一種是對象類型,這兩種都經常使用,非常重要,我們先來看一下什么是基礎靜態類型。
基礎靜態類型
基礎靜態類型非常簡單,只要在聲明變量的后邊加一個:號,然后加上對應的類型哦。比如下面的代碼,就是聲明了一個數字類型的變量,叫做count。
const count : number = 918; const myName :string = 'jspang' 復制代碼類似這樣常用的基礎類型還有,我這里就舉幾個最常用的哦,null,undefinde,symbol,boolean,void這些都是最常用的基礎數據類型,至于例子,我這里就不詳細的寫了,后面碰到,我們再繼續講解。
對象類型
我們先來看一個例子,通過例子有經驗的小伙伴就知道個大概了,然后我們再來講解(其實上節課我們也講到了,我們這里就當復習了)。新建一個文件demo3.ts(你可以跟我不一樣),然后寫下如下代碼。
const xiaoJieJie: {name: string,age: number, } = {name: "大腳",age: 18, }; console.log(xiaoJieJie.name); 復制代碼寫完后,我們在terminal(終端)中輸入ts-node demo3.ts,可以看到結果輸出了大腳。這就是一個經典的對象類型,也是最簡單的對象類型。對象類型也可以是數組,比如現在我們需要很多小姐姐,我們就可以這樣寫。
const xiaoJieJies: String[] = ["謝大腳", "劉英", "小紅"]; 復制代碼這時候的意思是,變量xiaoJieJies必須是一個數組,數組里的內容必須是字符串。你可以試著把字符串改為數字,VSCode會直接給我們報錯。
const xiaoJieJies: String[] = ["謝大腳", "劉英", 123]; 復制代碼現在都講究面向對象編程,我這面向對象編程這么多年了,也沒再多編出來一個。我們再來看看下面的代碼。這個代碼就是用類的形式,來定義變量。
class Person {} const dajiao: Person = new Person(); 復制代碼這個意思就是dajiao必須是一個Person類對應的對象才可以。我們還可以定義一個函數類型,并確定返回值。代碼如下:
const jianXiaoJieJie: () => string = () => {return "大腳"; }; 復制代碼那我們現在總結一下對象類型可以有幾種形式:
- 對象類型
- 數組類型
- 類類型
- 函數類型
這幾種形式我們在TypeScript里叫做對象類型。
這節課我們就主要學習了基礎類型和對象類型的概念,希望小伙伴都能學會,再次提醒,動手練習會有更好的效果。小伙伴們,加油。
04.TypeScript 中的類型注釋和類型推斷
這節課我們學習一下 TypeScript 中的兩個基本概念:類型注解和類型推斷,這兩個概念在我們編寫 TypeScript 代碼時會一直使用(重點),但很多教程都沒有講解,不過在官方文檔中有比較好的解釋。你現在可能還不能完全理解我說的這兩個概念,但是你看完文章后就會有很直觀的了解啦。
type annotation 類型注解
這個概念我們在前三節課中一直使用,只是我沒明確這個概念和關系,所以你會覺的很陌生。這就好比,你身邊有一個特別漂亮的姑娘,她一直很喜歡你,你也很喜歡她,但窗戶紙一直沒捅破,直到有一天你們喝多后,去了如家酒店(談了談心),你們的關系就明確了。
學程序并沒有這么復雜,我們直接點,新建一個文件demo4.ts ,然后看代碼:
let count: number; count = 123; 復制代碼這段代碼就是類型注解,意思是顯示的告訴代碼,我們的count 變量就是一個數字類型,這就叫做類型注解。是不是一下就明白了,其實程序這東西就這么簡單,真正復雜的是人。
type inferrence 類型推斷
當你明白了類型注解的概念之后,再學類型推斷就更簡單了,先來看一段代碼。還是在Demo4.ts文件中寫入下面的代碼。
let countInference = 123; 復制代碼這時候我并沒有顯示的告訴你變量countInference是一個數字類型,但是如果你把鼠標放到變量上時,你會發現 TypeScript 自動把變量注釋為了number(數字)類型,也就是說它是有某種推斷能力的,通過你的代碼 TS 會自動的去嘗試分析變量的類型。這個就彷佛是人的情商比較高,還沒等女生表白那,你就已經看出她的心思。
工作使用問題(潛規則)
- 如果 TS 能夠自動分析變量類型, 我們就什么也不需要做了
- 如果 TS 無法分析變量類型的話, 我們就需要使用類型注解
先來看一個不用寫類型注解的例子:
const one = 1; const two = 2; const three = one + two; 復制代碼再來看一個用寫類型注解的例子:
function getTotal(one, two) {return one + two; }const total = getTotal(1, 2); 復制代碼這種形式,就需要用到類型注釋了,因為這里的one和two會顯示為any類型。這時候如果你傳字符串,你的業務邏輯就是錯誤的,所以你必須加一個類型注解,把上面的代碼寫成下面的樣子。
function getTotal(one: number, two: number) {return one + two; }const total = getTotal(1, 2); 復制代碼這里有的一個問題是,為什么total這個變量不需要加類型注解,因為當one和two兩個變量加上注解后,TypeScript 就可以自動通過類型推斷,分析出變量的類型。
當然 TypeScript 也可以推斷出對象中屬性的類型,比如現在寫一個小姐姐的對象,然后里邊有兩個屬性。
const XiaoJieJie = {name: "劉英",age: 18, }; 復制代碼寫完后你把鼠標放在XiaoJieJie對象上面,就會提示出他里邊的屬性,這表明 TypeScript 也分析出了對象的屬性的類型。
在寫 TypeScript 代碼的一個重要宗旨就是每個變量,每個對象的屬性類型都應該是固定的,如果你推斷就讓它推斷,推斷不出來的時候你要進行注釋。
05.TypeScript 函數參數和返回類型定義
這節主要學習一下函數的參數類型定義和返回值的定義,學完這節內容后,你會對函數的參數和返回值類型定義都有通透的了解。
簡單類型定義
上節課我們寫了一個getTotal的函數,并且對傳入的參數作了定義,我們再復習一遍。
新建一個文件demo5.ts,然后寫入代碼
function getTotal(one: number, two: number) {return one + two; }const total = getTotal(1, 2); 復制代碼這時候我們寫的代碼其實是有一個小坑的,就是我們并沒有定義getTotal的返回值類型,雖然TypeScript可以自己推斷出返回值是number類型。 但是如果這時候我們的代碼寫錯了,比如寫程了下面這個樣子。
function getTotal(one: number, two: number) {return one + two + ""; }const total = getTotal(1, 2); 復制代碼這時候total的值就不是number類型了,但是不會報錯。有的小伙伴這時候可能會說,可以直接給total一個類型注解,比如寫成這個樣子。
const total: number = getTotal(1, 2); 復制代碼這樣寫雖然可以讓編輯器報錯,但是這還不是很高明的算法,因為你沒有找到錯誤的根本,這時錯誤的根本是getTotal()函數的錯誤,所以合適的做法是給函數的返回值加上類型注解,代碼如下:
function getTotal(one: number, two: number): number {return one + two; }const total = getTotal(1, 2); 復制代碼這段代碼就比較嚴謹了,所以小伙伴們在寫代碼時,盡量讓自己的代碼更加嚴謹。
函數無返回值時定義方法
有時候函數是沒有返回值的,比如現在定義一個sayHello的函數,這個函數只是簡單的terminal打印,并沒有返回值。
function sayHello() {console.log("hello world"); } 復制代碼這就是沒有返回值的函數,我們就可以給他一個類型注解void,代表沒有任何返回值。
function sayHello(): void {console.log("hello world"); } 復制代碼如果這樣定義后,你再加入任何返回值,程序都會報錯。
never 返回值類型
如果一個函數是永遠也執行不完的,就可以定義返回值為never,那什么樣的函數是永遠也執行不完的那?我們先來寫一個這樣的函數(比如執行執行的時候,拋出了異常,這時候就無法執行完了)。
function errorFuntion(): never {throw new Error();console.log("Hello World"); } 復制代碼還有一種是一直循環,也是我們常說的死循環,這樣也運行不完,比如下面的代碼:
function forNever(): never {while (true) {}console.log("Hello JSPang"); } 復制代碼函數參數為對象(解構)時
這個坑有很多小伙伴掉下去過,就是當一個函數的參數是對象時,我們如何定義參數對象的屬性類型。我先寫個一般javaScript的寫法。
function add({ one, two }) {return one + two; }const total = add({ one: 1, two: 2 }); 復制代碼在瀏覽器中你會看到直接報錯了,意思是total有可能會是任何類型,那我們要如何給這樣的參數加類型注解那?最初你可能會這樣寫。
function add({ one: number, two: number }) {return one + two; }const total = add({ one: 1, two: 2 }); 復制代碼你在編輯器中會看到這種寫法是完全錯誤的。那正確的寫法應該是這樣的。
function add({ one, two }: { one: number, two: number }): number {return one + two; }const three = add({ one: 1, two: 2 }); 復制代碼如果參數是對象,并且里邊只有一個屬性時,我們更容易寫錯。 錯誤代碼如下:
function getNumber({ one }: number) {return one; }const one = getNumber({ one: 1 }); 復制代碼看著好像沒什么問題,但實際這是有問題的,正確的代碼應該時這樣的。
function getNumber({ one }: { one: number }): number {return one; }const one = getNumber({ one: 1 }); 復制代碼這樣寫才是正確的寫法,小伙伴們趕快動手練習一下吧,剛開始學你可能會覺的很麻煩,但是你寫的時間長了,你就會發現有規矩還是好的。人向往自由,缺鮮有人能屈駕自由。
06.TypeScript 中數組類型的定義
這節課學習一下 TypeScript 中的數組類型,一般的數組類型定義我們已經接觸過了,只是沒有單獨拿出來講,所以先來復習一下。
一般數組類型的定義
現在我們可以定義一個最簡單的數組類型,比如就是數字類型,那么就可以這么寫:
const numberArr = [1, 2, 3]; 復制代碼這時候你把鼠標放在numberArr上面可以看出,這個數組的類型就是 number 類型。這是 TypeScript 通過類型推斷自己推斷出來的。 如果你要顯示的注解,也非常簡單,可以寫成下面的形式。
const numberArr: number[] = [1, 2, 3]; 復制代碼同樣道理,如果你的數組各項是字符串,你就可以寫成這樣。
const stringArr: string[] = ["a", "b", "c"]; 復制代碼也就是說你可以定義任意類型的數組,比如是undefined。
const undefinedArr: undefined[] = [undefined, undefined]; 復制代碼這時候問題來了,如果數組中有多種類型,比如既有數字類型,又有字符串的時候。那我們要如何定義那。 很簡單,只要加個(),然后在里邊加上|就可以了,具體看代碼。
const arr: (number | string)[] = [1, "string", 2]; 復制代碼數組簡單類型的定義就是這樣了,并不難。
數組中對象類型的定義
如果你作過一些項目,你就會知道真實的項目中數組中一定會有對象的出現。那對于這類帶有對象的數組定義就稍微麻煩點了。 比如現在我們要定義一個有很多小姐姐的數組,每一個小姐姐都是一個對象。這是的定義就編程了這樣。
const xiaoJieJies: { name: string, age: Number }[] = [{ name: "劉英", age: 18 },{ name: "謝大腳", age: 28 }, ]; 復制代碼這種形式看起來比較麻煩,而且如果有同樣類型的數組,寫代碼也比較麻煩,TypeScript 為我們準備了一個概念,叫做類型別名(type alias)。
比如剛才的代碼,就可以定義一個類型別名,定義別名的時候要以type關鍵字開始。現在定義一個Lady的別名。
type Lady = { name: string, age: Number }; 復制代碼有了這樣的類型別名以后哦,就可以把上面的代碼改為下面的形式了。
type Lady = { name: string, age: Number };const xiaoJieJies: Lady[] = [{ name: "劉英", age: 18 },{ name: "謝大腳", age: 28 }, ]; 復制代碼這樣定義是完全起作用的,比如我們下面在對象里再加入一個屬性,這時候編譯器就會直接給我們報錯了。
這時候有的小伙伴就會問了,我用類進行定義可以嗎?答案是可以的,比如我們定義一個Madam的類,然后用這個類來限制數組的類型也是可以的。
class Madam {name: string;age: number; }const xiaoJieJies: Madam[] = [{ name: "劉英", age: 18 },{ name: "謝大腳", age: 28 }, ]; 復制代碼可以看到結果,我們這么寫也是完全可以的。
這就是這節課的所有內容了,希望小伙伴們可以學會,并動手練習一下。
07.TypeScript 中元組的使用和類型約束
TypeScript 中提供了元組的概念,這個概念是JavaScript中沒有的。但是不要慌張,其實元組在開發中并不常用,也可能是我的精力還不夠。一般只在數據源是CVS這種文件的時候,會使用元組。其實你可以把元組看成數組的一個加強,它可以更好的控制或者說規范里邊的類型。
元組的基本應用
我們先來看一個數組和這個數組注解的缺點,比如我們有一個小姐姐數組,數組中有姓名、職業和年齡,代碼如下:
const xiaojiejie = ["dajiao", "teacher", 28]; 復制代碼這時候把鼠標放到xiaojiejie變量上面,可以看出推斷出來的類型。我們就用類型注解的形式給他作一個注解,代碼如下:
const xiaojiejie: (string | number)[] = ["dajiao", "teacher", 28]; 復制代碼這時候你已經增加了代碼注解,但是這并不能很好的限制,比如我們把代碼改成下面的樣子,TypeScript依然不會報錯。
const xiaojiejie: (string | number)[] = ["dajiao", 28, "teacher"]; 復制代碼我們只是簡單的把數組中的位置調換了一下,但是TypeScript并不能發現問題,這時候我們需要一個更強大的類型,來解決這個問題,這就是元組。
元組和數組類似,但是類型注解時會不一樣。
const xiaojiejie: [string, string, number] = ["dajiao", "teacher", 28]; 復制代碼這時候我們就把數組中的每個元素類型的位置給固定住了,這就叫做元組。
元組的使用
目前我的工作中不經常使用元組,因為如果要使用元組,完全可以使用對象的形式來代替,但是如果你維護老系統,你會發現有一種數據源時CSV,這種文件提供的就是用逗號隔開的,如果要嚴謹的編程就需要用到元組了。例如我們有這樣一組由CSV提供的(注意這里只是模擬數據)。
"dajiao", "teacher", 28; "liuying", "teacher", 18; "cuihua", "teacher", 25; 復制代碼如果數據源得到的數據時這樣的,你就可以使用元組了。
const xiaojiejies: [string, string, number][] = [["dajiao", "teacher", 28],["liuying", "teacher", 18],["cuihua", "teacher", 25], ]; 復制代碼這節課你的主要內容是,你要搞清楚元組和數組的區別,在理解后能在項目中適當的時候使用不同的類型。
08.TypeScript 中的 interface 接口
現在紅浪漫洗浴中心要開始招聘小姐姐了,這時候你需要一些小姐姐的簡歷投遞和自動篩選功能,就是不符合簡歷要求的會直接被篩選掉,符合的才可以進入下一輪的面試。那最好的解決方法就是寫一個接口。TypeScript 中的接口,就是用來規范類型的。
Interface 接口初步了解
現在我們要作一個簡歷的自動篩選程序,很簡單。年齡小于 25 歲,胸圍大于 90 公分的,可以進入面試環節。我們最開始的寫法是這樣的。(新建一個文件 Demo8.ts,然后編寫如下代碼)
const screenResume = (name: string, age: number, bust: number) => {age < 24 && bust >= 90 && console.log(name + "進入面試");age > 24 || (bust < 90 && console.log(name + "你被淘汰")); };screenResume("大腳", 18, 94); 復制代碼寫好后,好像我們的程序寫的不錯,可以在終端中使用ts-node demo8.ts進行查看。這時候老板又增加了需求,說我必須能看到這些女孩的簡歷。于是你又寫了這樣一個方法。
const getResume = (name: string, age: number, bust: number) => {console.log(name + "年齡是:" + age);console.log(name + "胸圍是:" + bust); }; getResume("大腳", 18, 94); 復制代碼這時候問題來了,程序開發中一直強調“代碼重用”,兩個方法用的類型注解一樣,需要作個統一的約束。大上節課我們學了一個類型別名的知識可以解決代碼重復的問題,這節課我們就學習一個更常用的語法接口(Interface).
我們可以把這兩個重復的類型注解,定義成統一的接口。代碼如下:
interface Girl {name: string;age: number;bust: number; } 復制代碼有了接口后,我們的程序也要作一些修改,需要寫成下面的樣子。這樣就更像是面向對象編程了。
const screenResume = (girl: Girl) => {girl.age < 24 && girl.bust >= 90 && console.log(girl.name + "進入面試");girl.age > 24 || (girl.bust < 90 && console.log(girl.name + "你被淘汰")); };const getResume = (girl: Girl) => {console.log(girl.name + "年齡是:" + girl.age);console.log(girl.name + "胸圍是:" + girl.bust); }; const girl = {name: "大腳",age: 18,bust: 94, };screenResume(girl); getResume(girl); 復制代碼這時候我們代碼就顯得專業了很多,以后再用到同樣的接口也不怕了,直接使用girl就可以了。
接口和類型別名的區別
現在我們學了接口,也學過了類型別名,這兩個語法和用處好像一樣,我先表個態,確實用起來基本一樣,但是也有少許的不同。
類型別名可以直接給類型,比如string,而接口必須代表對象。
比如我們的類型別名可以寫出下面的代碼:
type Girl1 = stirng; 復制代碼但是接口就不能這樣寫,它必須代表的是一個對象,也就是說,你初始化girl的時候,必須寫出下面的形式.
const girl = {name: "大腳",age: 18,bust: 94, }; 復制代碼接口非必選值得定義
這節課我們多學一點,因為接口這里的知識點還是挺多的。比如這時候老板又有了新的要求,要求盡量能看到小姐姐的腰圍,但是不作強制要求,就是可選值嗎。那接口如何定義那?其實typeScript已經為我們準備好了相應的辦法,就是在:號前加一個?
比如把Girl的接口寫成這樣。
interface Girl {name: string;age: number;bust: number;waistline?: number; } 復制代碼然后我們再修改一下getResume方法,寫成這樣。
const getResume = (girl: Girl) => {console.log(girl.name + "年齡是:" + girl.age);console.log(girl.name + "胸圍是:" + girl.bust);girl.waistline && console.log(girl.name + "腰圍是:" + girl.waistline); }; 復制代碼這時候在定義girl對象的時候,就可以寫saistline(腰圍),也可以不寫了。
好了,這節課就先到這里,Interface(接口)的知識并沒有講完,我們下節課接著講。
09.TypeScript 中的 interface 接口 2
我們接著上節課繼續講接口,接口部分的內容還是比較多的。所以小伙伴們不要著急,我們馬上開始學習。
允許加入任意值
簡歷一般是有自由發揮的空間的,所以這時候需要一些任意值,就是自己愿意寫什么就寫什么。這時候interface接口也是支持的。方法如下: 我們接著上節課的代碼,新建一個Demo9.ts,然后把上節課代碼拷貝過來。
interface Girl {name: string;age: number;bust: number;waistline?: number;[propname: string]: any; } 復制代碼這個的意思是,屬性的名字是字符串類型,屬性的值可以是任何類型。
這時候我們在對象里給一個性別,代碼如下:
const girl = {name: "大腳",age: 18,bust: 94,waistline: 21,sex: "女", }; 復制代碼再修改一下代碼,這首就沒有錯誤了。
const getResume = (girl: Girl) => {console.log(girl.name + "年齡是:" + girl.age);console.log(girl.name + "胸圍是:" + girl.bust);girl.waistline && console.log(girl.name + "腰圍是:" + girl.waistline);girl.sex && console.log(girl.name + "性別是:" + girl.sex); }; 復制代碼這時候我們的程序是不報錯的,但是如果我們去掉剛才的設置,就會報錯。
[propname:string]:any; //去掉 復制代碼接口里的方法
接口里不僅可以存屬性,還可以存方法,比如這時候有個say()方法,返回值是string類型。這時候你就不要再想成簡歷了,你需要更面向對象化的編程,想象成一個人。
interface Girl {name: string;age: number;bust: number;waistline?: number;[propname: string]: any;say(): string; } 復制代碼加上這個say()方法后,程序馬上就會報錯,因為我們對象里沒有 say 方法。那我們就要給對象一個 say 方法
const girl = {name: "大腳",age: 18,bust: 94,waistline: 21,sex: "女",say() {return "歡迎光臨 ,紅浪漫洗浴!!";}, }; 復制代碼接口和類的約束
我們都知道 JavaScript 從ES6里是有類這個概念的,類可以和接口很好的結合,我們先來看一個例子。下面的
class XiaoJieJie implements Girl {} 復制代碼這時候類會直接報錯,所以我們需要把這個類寫的完全點。
class XiaoJieJie implements Girl {name = "劉英";age = 18;bust = 90;say() {return "歡迎光臨 ,紅浪漫洗浴!!";} } 復制代碼接口間的繼承
接口也可以用于繼承的,比如你新寫一個Teacher接口,繼承于Person接口。
interface Teacher extends Girl {teach(): string; } 復制代碼比如這時候老板說了,只看 Teacher 級別的簡歷,那我們需要修改getResume()方法。
const getResume = (girl: Teacher) => {console.log(girl.name + "年齡是:" + girl.age);console.log(girl.name + "胸圍是:" + girl.bust);girl.waistline && console.log(girl.name + "腰圍是:" + girl.waistline);girl.sex && console.log(girl.name + "性別是:" + girl.sex); }; 復制代碼修改后,你就會發現下面我們調用getResume()方法的地方報錯了,因為這時候傳的值必須有Teach方法,
getResume(girl); 復制代碼修改girle對象,增加teach()方法,這時候就不會報錯了。
const girl = {name: "大腳",age: 18,bust: 94,waistline: 21,sex: "女",say() {return "歡迎光臨 ,紅浪漫洗浴!!";},teach() {return "我是一個老師";}, }; 復制代碼關于接口的知識就講到這里吧,這基本包含了接口 80%的知識,還有些基本不用的語法,我就不講了。如果課程中遇到,我們再講。學會了接口,你還需要明白一件事,就是接口只是對我們開發的約束,在生產環境中并沒有體現。也可以說接口只是在 TypeScript 里幫我們作語法校驗的工具,編譯成正式的js代碼,就不會有任何用處了。
10.TypeScript 中類的概念和使用
前幾天腰疾又犯,寢食難安,有所耽誤,現稍有好轉,繼續錄課。TypeScript 中類的概念和 ES6 中原生類的概念大部分相同,但是也額外增加了一些新的特性。我在這里會完全從一個新手的角度,講解類的各項知識點。
類的基本使用
新建一個文件,叫做demo10.ts,然后定義一個最簡單的Lady類,這里要使用關鍵字class,類里邊有姓名屬性和一個得到姓名的方法,代碼如下:
class Lady {content = "Hi,帥哥";sayHello() {return this.content;} }const goddess = new Lady(); console.log(goddess.sayHello()); 復制代碼寫完代碼后,可以使用ts-node demo10.ts來查看一下結果。
這是一個最簡單的類了,如果你有些編程經驗,對這個一定很熟悉,工作中幾乎每天都會用到。
類的繼承
這里提前說一下 TypeScrip 的繼承和ES6中的繼承是一樣的。關鍵字也是extends,比如我們這里新建一個XiaoJieJie的類,然后繼承自Lady類,在XiaoJieJie類里寫一個新的方法,叫做sayLove,具體代碼如下。
class Lady {content = "Hi,帥哥";sayHello() {return this.content;} } class XiaoJieJie extends Lady {sayLove() {return "I love you";} }const goddess = new XiaoJieJie(); console.log(goddess.sayHello()); console.log(goddess.sayLove()); 復制代碼類寫好以后,我們聲明的對象是XiaoJieJie這個類,我們同時執行sayHello()和sayLove()都是可以執行到的,這說明繼承起作用了。
類的重寫
講了繼承,那就必須繼續講講重寫,重寫就是子類可以重新編寫父類里邊的代碼。現在我們在XiaoJieJie這個類里重寫父類的sayHello()方法,比如現在我們覺的叫的不夠親切,我們改成下面這個樣子。
class XiaoJieJie extends Lady {sayLove() {return "I love you!";}sayHello() {return "Hi , honey!";} } 復制代碼然后我們再次運行ts-node demo10.ts來查看結果。
super 關鍵字的使用
我們再多講一點,就是super關鍵字的使用,比如我們還是想使用Lady類中說的話,但是在后面,加上你好兩個字就可以了。這時候就可以使用super關鍵字,它代表父類中的方法。那我們的代碼就可以寫成這個樣子了。
class XiaoJieJie extends Lady {sayLove() {return "I love you!";}sayHello() {return super.sayHello() + "。你好!";} } 復制代碼通過這節課我們至少要知道TypoeScript中的類是如何定義和繼承的。類中還有很多知識點要講,這節課先到這里,下節課繼續。
11.TypeScript 中類的訪問類型
上節已經簡單學習了TypeScript中類的使用,這節我們繼續學習一下類中的訪問類型。其實類的訪問類型就是基于三個關鍵詞private、protected和public,也是三種訪問類型,這節課的主要內容也是講一下這三個訪問類型如何使用,都代表什么意思。
先寫一個簡單的類
我們新建一個Demo11.ts文件,然后注釋掉以前寫的代碼,防止由于重名而產生沖突。在新的文件里,我們定義一個 Person 類,然后使用這個類的對象,進行賦值,最后打印在控制臺上。具體代碼如下:
class Person {name: string; }const person = new Person(); person.name = "jspang.com";console.log(person.name); 復制代碼寫完后我們直接可以在Terminal(中),輸入ts-node demo11.ts進行查看結果,結果會打印出jspang.com。
public 訪問屬性講解
這時候可以打出jspang.com是因為我們如果不在類里對name的訪問屬性進行定義,那么它就會默認是public訪問屬性。
這就相當于下面的這段代碼:
class Person {public name:string; } 復制代碼public從英文字面的解釋就是公共的或者說是公眾的,在程序里的意思就是允許在類的內部和外部被調用.
比如我們在類內調用,我們在寫一個sayHello的方法,代碼如下:
class Person {public name:string;public sayHello(){console.log(this.name + ' say Hello')} }復制代碼這是的this.name就是類的內部調用。我們在下面在執行一下這個方法person.sayHello(),終端中可以看到一切正常運行了,順利打印出了jspang.com say Hello這句話。
在類的外部調用,我們就可以很簡單的看出來了,比如下面的代碼,從注釋橫線下,全部是類的外部。
class Person {public name:string;public sayHello(){console.log(this.name + 'say Hello')} } //-------以下屬于類的外部-------- const person = new Person() person.name = 'jspang.com' person.sayHello() console.log(person.name)復制代碼結果我就不演示了,一定是可以被調用的,接下來我們再來看private屬性。
private 訪問屬性講解
private 訪問屬性的意思是,只允許再類的內部被調用,外部不允許調用
比如現在我們把 name 屬性改成private,這時候在類的內部使用不會提示錯誤,而外部使用VSCode直接會報錯。
class Person {private name:string;public sayHello(){console.log(this.name + 'say Hello') //此處不報錯} } //-------以下屬于類的外部-------- const person = new Person() person.name = 'jspang.com' //此處報錯 person.sayHello() console.log(person.name) //此處報錯復制代碼protected 訪問屬性講解
protected 允許在類內及繼承的子類中使用
做一個例子,把name的訪問屬性換成protected,這時候外部調用name的代碼會報錯,內部的不會報錯,和private一樣。這時候我們再寫一個Teacher類,繼承于Person,代碼如下:
class Person {protected name:string;public sayHello(){console.log(this.name + 'say Hello') //此處不報錯} }class Teacher extends Person{public sayBye(){this.name;} } 復制代碼這時候在子類中使用this.name是不報錯的。
通過這個小例子相信你一定指導什么是類的內部和類的外部,也知道了三個訪問類型的區別了。其實我最開始學 java 的時候,這個概念還真的比較難懂,但是你先掌握了類內和類外的概念后,這三個訪問類型就非常好理解了。
12.TypeScript 類的構造函數
這節課繼續學習類的知識,如果你學過Java的話,對構造函數一定不陌生,構造函數就是在類被初始化的時候,自動執行的一個方法。我們通過這個構造方法經常作很多需要提前完成的工作,比如顯示頁面前我們要從后臺得到數據。直接看例子。
類的構造函數
新建立一個頁面Demo12.ts,然后在頁面里新建一個 Person 類,類的里邊定義一個name,但是name我們并不給他值,然后我們希望在new出對象的時候,直接通過傳遞參數的形式,給name賦值,并打印出來。這時候我們就需要用到構造函數了,構造函數的關鍵字是constructor。
class Person{public name :string ;constructor(name:string){this.name=name}}const person= new Person('jspang') console.log(person.name) 復制代碼寫完后使用ts-node demo12.ts進行查看,應該可以打出jspang的字樣。這是最常規和好理解的寫法,那有沒有更簡單的寫法那?當然有。
class Person{constructor(public name:string){} }const person= new Person('jspang') console.log(person.name) 復制代碼這種寫法就相當于你定義了一個name,然后在構造函數里進行了賦值,這是一種簡化的語法,在工作中我們使用這種語法的時候會更多一些。
類繼承中的構造器寫法
普通類的構造器我們已經會了,在子類中使用構造函數需要用super()調用父類的構造函數。這時候你可能不太理解我說的話,我們還是通過代碼來說明(詳細說明在視頻中講述)。
class Person{constructor(public name:string){} }class Teacher extends Person{constructor(public age:number){super('jspang')} }const teacher = new Teacher(18) console.log(teacher.age) console.log(teacher.name) 復制代碼這就是子類繼承父類并有構造函數的原則,就是在子類里寫構造函數時,必須用super()調用父類的構造函數,如果需要傳值,也必須進行傳值操作。就是是父類沒有構造函數,子類也要使用super()進行調用,否則就會報錯。
class Person{}class Teacher extends Person{constructor(public age:number){super()} }const teacher = new Teacher(18) console.log(teacher.age)復制代碼好了,這就是這節課我們所學的內容了,主要講的就是類中的構造函數(也有叫構造器的),構造函數在工作中用的很多,所以你要學會并作充分的練習。
13.TypeScript 類的 Getter、Setter 和 static 使用
有小伙伴留言,學了類的訪問類型private,那這個東西如何使用?其實他的最大用處是封裝一個屬性,然后通過 Getter 和 Setter 的形式來訪問和修改這個屬性。
類的 Getter 和 Setter
我們新建一個文件Demo13.ts,然后聲明一個XiaoJieJie(小姐姐)類,都知道小姐姐的年齡是不能隨便告訴人,所以使用了private,這樣別人就都不知道她的真實年齡,而只有她自己知道。
代碼如下:
class Xiaojiejie {constructor(private _age:number){} }復制代碼如果別人想知道,就必須通過getter屬性知道,注意我這里用的是屬性,對他就是一個屬性。getter屬性的關鍵字是get,后邊跟著類似方法的東西,但是你要注意,它并不是方法,歸根到底還是屬性。
class Xiaojiejie {constructor(private _age:number){}get age(){return this._age} }const dajiao = new Xiaojiejie(28)console.log(dajiao.getAge)復制代碼這時候你會覺的這么寫不是多此一舉嗎?玄妙就在于getter里,我們可以對_age進行處理,比如別人問的時候我們就偷摸的減少 10 歲。代碼可以寫成這樣。
class Xiaojiejie {constructor(private _age:number){}get age(){return this._age-10} }復制代碼這時候大腳的年齡就編程了迷人的 18 歲,是不是通過這個小例子,一下子就明白了private和getter的用處。 _age是私有的,那類的外部就沒辦法改變,所以這時候可以用setter屬性進行改變,代碼如下:
class Xiaojiejie {constructor(private _age:number){}get age(){return this._age-10}set age(age:number){this._age=age} }const dajiao = new Xiaojiejie(28) dajiao.age=25 console.log(dajiao.age) 復制代碼其實setter也是可以保護私有變量的,現在大腳的年齡輸出是 15 歲,這肯定不行,不符合法律哦,這樣是我們在setter里給他加上個 3 歲,就可以了。
set age(age:number){this._age=age+3}復制代碼這是想通過這個例子讓小伙伴們清楚的明白getter和setter的使用,很多小伙伴剛學這部分,都不太清楚為什么要使用getter和setter ,你也能更清楚private訪問類型的意義。
類中的 static
學習類,都知道要想使用這個類的實例,就要先New出來(),但有時候人們就是喜歡走捷徑,在們有對象的情況下,也想享受青春的躁動,有沒有方法?肯定是有方法的。
比如我們先寫一下最常規的寫法:
class Girl {sayLove() {return "I Love you";} }const girl = new Girl(); console.log(girl.sayLove()); 復制代碼但是現在你不想new出對象,而直接使用這個方法,那TypeScript為你提供了快捷的方式,用static聲明的屬性和方法,不需要進行聲明對象,就可以直接使用,代碼如下。
class Girl {static sayLove() {return "I Love you";} } console.log(Girl.sayLove()); 復制代碼這節課我們就學到了這里,復習一下,我們學了private的使用意義,學了getter和setter屬性,還學習了靜態修飾符static,這樣就不用 new 出對象就可以使用類里的方法了。
14. 類的只讀屬性和抽象類
這節主要講一下類里的一個概念就是抽象類,抽象類很父類很像,都需要繼承,但是抽象類里一般都有抽象方法。繼承抽象類的類必須實現抽象方法才可以。在講抽象類之前,我想把上節課我遺忘的一個知識點給大家不上,那就是類里的只讀屬性readonly.
類里的只讀屬性 readonly
新建一個文件Demo14.ts,然后寫下面一個類,并進行實例化和賦值操作,代碼如下:
class Person {constructor(public name:string ){ } }const person = new Person('jspang') console.log(person.name)復制代碼寫完后我們可以在終端(Terminal)中看一下結果,結果就應該是jspang。
比如我現在有一個需求,就是在實例化對象時賦予的名字,以后不能再更改了,也就是我們常說的只讀屬性。我們先來看現在這種情況是可以隨意更改的,比如我寫下面的代碼。
class Person {constructor(public name:string ){ } }const person = new Person('jspang') person.name= '謝廣坤' console.log(person.name) 復制代碼這時候就可以用一個關鍵詞readonly,也就是只讀的意思,來修改Person類代碼。
class Person {public readonly _name :string;constructor(name:string ){this._name = name;} }const person = new Person('jspang') person._name= '謝廣坤' console.log(person._name)復制代碼這樣寫完后,VSCode就回直接給我們報錯,告訴我們_name屬性是只讀屬性,不能修改。這是上節課遺忘的一個知識點,我在這里給你補上了。
抽象類的使用
什么是抽象類那?我給大家舉個例子,比如我開了一個紅浪漫洗浴中心,里邊有服務員,有初級技師,高級技師,每一個崗位我都寫成一個類,那代碼就是這樣的。(注釋掉剛才寫的代碼)
class Waiter {}class BaseTeacher {}class seniorTeacher {} 復制代碼我作為老板,我要求無論是什么職位,都要有獨特的技能,比如服務員就是給顧客倒水,初級技師要求會泰式按摩,高級技師要求會 SPA 全身按摩。這是一個硬性要求,但是每個職位的技能有不同,這時候就可以用抽象類來解決問題。
抽象類的關鍵詞是abstract,里邊的抽象方法也是abstract開頭的,現在我們就寫一個Girl的抽象類。
abstract class Girl{abstract skill() //因為沒有具體的方法,所以我們這里不寫括號}復制代碼有了這個抽象類,三個類就可以繼承這個類,然后會要求必須實現skill()方法,代碼如下:
abstract class Girl{abstract skill() //因為沒有具體的方法,所以我們這里不寫括號}class Waiter extends Girl{skill(){console.log('大爺,請喝水!')} }class BaseTeacher extends Girl{skill(){console.log('大爺,來個泰式按摩吧!')} }class seniorTeacher extends Girl{skill(){console.log('大爺,來個SPA全身按摩吧!')} }復制代碼我希望通過這個例子,你能對抽象類和抽象方法有一個比較深的認識。其實在工作中我們也會把這樣的需求用接口來實現。
15. 配置文件-初識 tsconfig.json
有人在 QQ 群里留言問我tsconfig.json是作什么的,我才意識到,我是應該詳細的講一下這個文件了,這個是 TypeScript 的文件,雖然不常用,但是很重要。有必要拿出幾節課詳細的講一下這個文件。
生成 tsconfig.json 文件
這個文件是通過tsc --init命令生成的,在桌面上新建一個文件夾TsDemo,然后打開VSCode,把文件托到編輯器中,然后打開終端Terminal,輸入tsc --init。
輸入完成后,就會出現tsconfig.json文件,你可以打開簡單的看一下,不過此時你可能看不懂。
其實它就是用來配置如何對ts文件進行編譯的,我們都叫它 typescript 的編譯配置文件。
如果此時你的tsc執行不了,很有可能是你沒有全局安裝 TypeScript,可以全局安裝一下。
讓 tsconfig.json 文件生效
你現在可以在文件夾跟目錄建立一個demo.ts文件,然后編寫一些最簡單的代碼,代碼如下:
const person: string = "jspang"; 復制代碼這時候我們不在使用ts-node直接執行了,需要用tsc demo.ts進行編譯,編譯后會得到demo.js文件。 生成的代碼如下:
var person = "jspang"; 復制代碼這時候好像一切都是正常的,但是我要告訴你的真相是tsconfig.json這個編譯配置文件并沒有生效。
此時我們打開tsconfig.json文件,找到complilerOptions屬性下的removeComments:true選項,把注釋去掉。
這個配置項的意思是,編譯時不顯示注釋,也就是編譯出來的js文件不顯示注釋內容。
現在你在文件中加入一些注釋,比如:
// I love jspang const person: string = "jspang"; 復制代碼這時候再運行編譯代碼tsc demo.ts,編譯后打開demo.js文件,你會發現注釋依然存在,說明tsconfig.json文件沒有起作用。
如果要想編譯配置文件起作用,我們可以直接運行tsc命令,這時候tsconfig.json才起作用,可以看到生成的js文件已經不帶注釋了。
include 、exclude 和 files
那現在又出現問題了,如果我們的跟目錄下有多個ts文件,我們卻只想編譯其中的一個文件時,如何作?
我們在項目根目錄,新建一個文件demo2.ts文件,然后也寫一段最簡單的 ts 代碼。
const person2: string = "jspang.com"; 復制代碼如果這時候我們在終端里運行tsc,雖然tsconfig.json生效了,但是兩個文件都被我們編譯了。這不是你想要的結果,我們可以用三種辦法解決這個問題。
include屬性是用來指定要編譯的文件的,比如現在我們只編譯demo.ts文件,而不編譯demo2.ts文件,就可以這樣寫。
寫配置文件時有個坑需要注意,就是配置文件不支持單引號,所以里邊都要使用雙引號。
{"include":["demo.ts"],"compilerOptions": {//any something//........} }復制代碼這時候再編譯,就只編譯demo.ts文件了。
include是包含的意思,exclude是不包含,除什么文件之外,意思是寫再這個屬性之外的而文件才進行編譯。比如你還是要編譯demo.ts文件,這時候的寫法就應該是這樣了。
{"exclude":["demo2.ts"],"compilerOptions": {//any something//........} } 復制代碼這樣寫依然只有demo.ts被編譯成了js文件。
files的配置效果和include幾乎一樣,我是沒找出有什么不同,只要配置到里邊的文件都可以編譯,如果有小伙伴知道有什么不同的,歡迎在視頻下方留言,然后一起學習。
{"files":["demo.ts"],"compilerOptions": {//any something//........} } 復制代碼結果是依然只有demo.ts文件被編譯。這節課我們就學到這里,目的只是讓大家初步了解一下tsconfig.js文件和它的使用方法,文件里邊還有很多配置項,這些我們都會逐步講到。
16. 配置文件-初識 compilerOptions 配置項
這節我們主要學習一下compilerOptions配置項,它是告訴TypeScript具體如何編譯成js文件的,里邊的配置項非常多,這節我們先來講幾個簡單的配置項,目的是讓你熟悉compilerOptions的使用方法。
removeComments 屬性
removeComments是complerOptions里的一個子屬性,它的用處是告訴TypeScript對編譯出來的js文件是否顯示注釋(注解)。比如我們現在把removeComments的值設置為true,就是在js中不顯示注釋。
我們把上節課文件沒有的Demo2.ts和生成的 JS 文件都刪除掉,只留Demo.ts文件,然后再Demo.ts文件里,加入一個注釋。
// I‘m JSPang const person: string = "jspang"; 復制代碼寫完注釋后,直接再終端Terminal里,輸入tsc,輸入完成后,很快就會生成一個demo.js文件,打開后會看到下面的代碼。
"use strict"; var person = "jspang"; 復制代碼你寫的注釋并沒有編譯到demo.js里。如果我們反之,把removeComments的值,設置為false,這時候demo.js里就會有注釋內容了。
"use strict"; // I‘m JSPang var person = "jspang"; 復制代碼strict 屬性
strict屬性如果設置為true,就代表我們的編譯和書寫規范,要按照TypeScript最嚴格的規范來寫,如果我們把這個設置為false或者注釋掉,意思是我們可以對設置一些不嚴格的寫法。
noImplicitAny 屬性
noImplicitAny屬性的作用是,允許你的注解類型 any 不用特意表明,只聽概念很難理解。這就是看我視頻的一個好處,如果你只看官方 API,你可能要迷糊一陣啥叫允許你的注解類型any不用特意表明,這就是每個漢字我都認識,連在一期就不知道啥意思的最好詮釋。
為了更好的說明,我們舉個例子,在demo.ts里,刪除剛才的代碼,然后寫一個方法,方法的參數我們設置成任意類型(any)。
function jspang(name) {return name; } 復制代碼這時候我們的TypeScript是進行報錯的,我們用tsc編譯也是報錯的。這就是因為我們開啟了strict:true,我們先注釋掉,然后把noImplicitAny的值設置為false,就不再報錯了。
如果設置為noImplicitAny:true,意思就是值就算是 any(任意值),你也要進行類型注釋。
function jspang(name: any) {return name; } 復制代碼你可以簡單的理解為,設置為 true,就是必須明確置頂 any 類型的值。
strictNullChecks 屬性
我們先把strictNullChecks設置為false,它的意思就是,**不強制檢查 NULL 類型。**我們舉個例子,讓你能一下子就明白,還是刪除demo.ts里的代碼,然后編寫代碼.
const jspang: string = null; 復制代碼代碼寫完后,你會發現這段代碼是不報錯的,如果是以前,一定是報錯的,這就是我們配置了“不強制檢驗 null 類型”。如果你設成strictNullChecks:true,這時候就報錯了。
ts-node 遵循 tsconfig.js 文件
有的小伙伴問我了,tsc fileName 是沒辦法遵循tsconfig.js文件的,那ts-node是否遵循?
這里直接告訴你答案,ts-node是遵循的,感興趣的可以自行試一下。
這節課我們就是簡單的認識一下compilerOptions屬性的配置,其實這些你只要掌握方法,并不需要記憶,我也是記不住每一項是干嘛的,用的時候會查 API 就可以了。下節課我們繼續學習配置文件。
17. 配置文件- compilerOptions 配置內容詳解
這節我們繼續講complierOptions里的配置項,里邊的內容很多,我只能選幾個重要的給大家講講,然后在這節最后,我會給出大家自己查詢的方法。需要再次說明的是,這些配置項沒必要記,因為他們真的不是每天都需要用到,所以你只要知道如何配置和重要的幾項,學會在自己需要時如何查詢就可以了。
rootDir 和 outDir
現在你的js文件直接編譯到了根目錄下,和ts文件混在了一起。我們當然是不喜歡這種方法的,工作中我們希望打包的js都生成在特定的一個文件夾里,比如build。
這時候你就可以通過配置outDir來配置,當然你也可以通過rootDir來指定ts文件的位置,比如我們把所有的 ts 文件都放到 src 下。那配置文件就應該這樣寫。
{"outDir": "./build" ,"rootDir": "./src" , } 復制代碼這時候你再在Terminal中輸入tsc,就會有不同的效果了。
編譯 ES6 語法到 ES5 語法-allowJs
現在你在src目錄下用ES6的語法寫了一個demo2.js文件,代碼如下。
export const name = "jspang"; 復制代碼如果你不做任何配置,這時候試用tsc是沒有效果的。你需要到tsconfig.js文件里進行修改,修改的地方有兩個。
"target":'es5' , // 這一項默認是開啟的,你必須要保證它的開啟,才能轉換成功 "allowJs":true, // 這個配置項的意思是聯通 復制代碼這兩項都開啟后,在使用tsc編譯時,就會編譯js文件了。
sourceMap 屬性
如果把sourceMap的注釋去掉,在打包的過程中就會給我們生成sourceMap文件.
sourceMap 簡單說,Source map 就是一個信息文件,里面儲存著位置信息。也就是說,轉換后的代碼的每一個位置,所對應的轉換前的位置。有了它,出錯的時候,除錯工具將直接顯示原始代碼,而不是轉換后的代碼。這無疑給開發者帶來了很大方便。
這里我不對 Source map 文件詳細講解,如果你感興趣,可以自行百度一下吧。
noUnusedLocals 和 noUnusedParameters
比如現在我們修改demo.ts文件的代碼,改為下面的樣子。
const jspang: string = null; export const name = "jspang"; 復制代碼這時候你會發現jspang這個變量沒有任何地方使用,但是我們編譯的話,它依然會被編譯出來,這就是一種資源的浪費。
//編譯后的文件 "use strict"; exports.__esModule = true; exports.name = void 0; var jspang = null; exports.name = "jspang"; 復制代碼這時候我們可以開啟noUnusedLocals:true,開啟后我們的程序會直接給我們提示不能這樣編寫代碼,有沒有使用的變量。
noUnusedParameters是針對于名優使用的函數的,方法和noUnusedLocals:true一樣,小伙伴們自己嘗試吧。
我們講了幾個最常用的方法,如果你需要全面的了解,可以查看這個網址:
www.tslang.cn/docs/handbo… (編譯選項詳解)
自己進行查看就可以了。
好了配置文件我們就暫時告一段落了,下節課我們講一下 TypeScript 里的聯合類型。
18. 聯合類型和類型保護
這節視頻將學習一下聯合類型和相關的類型保護知識,需要注意的是,只有聯合類型存在的情況下,才需要類型保護。普通的類型注解,并不需要我們這種特殊操作。那先來看一下什么是聯合類型。
聯合類型展示
所謂聯合類型,可以認為一個變量可能有兩種或兩種以上的類型。用代碼舉個例子,聲明兩個接口Waiter(服務員)接口和Teacher(技師)接口,然后在寫一個judgeWho(判斷是誰)的方法,里邊傳入一個animal(任意值),這時候可以能是Waiter,也可能是Teacher。所以我們使用了聯合類型,關鍵符號是|(豎線)。
interface Waiter {anjiao: boolean;say: () => {}; }interface Teacher {anjiao: boolean;skill: () => {}; }function judgeWho(animal: Waiter | Teacher) {} 復制代碼通過這個簡單的例子,你應該知道什么是聯合類型了。
function judgeWho(animal: Waiter | Teacher) {animal.say(); } 復制代碼但這時候問題來了,如果我直接寫一個這樣的方法,就會報錯,因為judgeWho不能準確的判斷聯合類型具體的實例是什么。
這時候就需要再引出一個概念叫做類型保護,類型保護有很多種方法,這節講幾個最常使用的。
類型保護-類型斷言
類型斷言就是通過斷言的方式確定傳遞過來的準確值,比如上面的程序,如果會anjiao(按腳),說明他就是技師,這時候就可以通過斷言animal as Teacher,然后直接調用skill方法,程序就不再報錯了。同樣如果不會按腳,說明就是不同的服務員,這時候調用say()方法,就不會報錯了。這就是通過斷言的方式進行類型保護。也是最常見的一種類型保護形式。具體看代碼:
interface Waiter {anjiao: boolean;say: () => {}; }interface Teacher {anjiao: boolean;skill: () => {}; }function judgeWho(animal: Waiter | Teacher) {if (animal.anjiao) {(animal as Teacher).skill();}else{(animal as Waiter).say();} }復制代碼類型保護-in 語法
我們還經常使用in語法來作類型保護,比如用if來判斷animal里有沒有skill()方法。
這里你可以賦值上面的judgeWho()方法,然后改一下名字,我這里改成了judgeWhoTwo()方法,具體程序如下:
function judgeWhoTwo(animal: Waiter | Teacher) {if ("skill" in animal) {animal.skill();} else {animal.say();} } 復制代碼這里的else部分能夠自動判斷,得益于TypeScript的自動判斷。
類型保護-typeof 語法
先來寫一個新的add方法,方法接收兩個參數,這兩個參數可以是數字number也可以是字符串string,如果我們不做任何的類型保護,只是相加,這時候就會報錯。代碼如下:
function add(first: string | number, second: string | number) {return first + second; } 復制代碼解決這個問題,就可以直接使用typeof來進行解決。
function add(first: string | number, second: string | number) {if (typeof first === "string" || typeof second === "string") {return `${first}${second}`;}return first + second; } 復制代碼像上面這樣寫,就不報錯了。這樣就可以進行繼續開心的編寫程序了。
類型保護-instanceof 語法
比如現在要作類型保護的是一個對象,這時候就可以使用instanceof語法來作。現在先寫一個NumberObj的類,代碼如下:
class NumberObj {count: number; } 復制代碼然后我們再寫一個addObj的方法,這時候傳遞過來的參數,可以是任意的object,也可以是NumberObj的實例,然后我們返回相加值,當然不進行類型保護,這段代碼一定是錯誤的。
function addObj(first: object | NumberObj, second: object | NumberObj) {return first.count + second.count; } 復制代碼報錯不要緊,直接使用instanceof語法進行判斷一下,就可以解決問題。
function addObj(first: object | NumberObj, second: object | NumberObj) {if (first instanceof NumberObj && second instanceof NumberObj) {return first.count + second.count;}return 0; } 復制代碼另外要說的是,instanceof 只能用在類上。這節課我介紹四種類型保護的方式,每種方式都在不同場景中使用(還有一些不太常用的類型保護方式,我就不講了),你需要自己深刻理解,多練習,在開發時才能靈活使用。
19. Enum 枚舉類型講解
這節主要學一下 TypeScript 中枚舉(enum)類型的使用,你如果在程序中能靈活的使用枚舉(enum),會讓程序有更好的可讀性。這里我拿每次去“大寶劍”點餐作個比喻。
一場大寶劍引出的思考
比如我現在去"大寶劍"時,通過擲色子隨機選擇一項服務,進行程序化模擬。這里我先用 JavaScript 的寫法來編寫。
初級程序員寫法:
function getServe(status: number) {if (status === 0) {return "massage";} else if (status === 1) {return "SPA";} else if (status === 2) {return "dabaojian";} } const result = getServe(0); console.log(`我要去${result}`); 復制代碼中級程序員寫法:
const Status = {MASSAGE: 0,SPA: 1,DABAOJIAN: 2, };function getServe(status: any) {if (status === Status.MASSAGE) {return "massage";} else if (status === Status.SPA) {return "spa";} else if (status === Status.DABAOJIAN) {return "dabaojian";} }const result = getServe(Status.SPA);console.log(`我要去${result}`); 復制代碼高級程序員寫法:
enum Status {MASSAGE,SPA,DABAOJIAN, }function getServe(status: any) {if (status === Status.MASSAGE) {return "massage";} else if (status === Status.SPA) {return "spa";} else if (status === Status.DABAOJIAN) {return "dabaojian";} }const result = getServe(Status.SPA);console.log(`我要去${result}`);復制代碼這時候我們就引出了今天的主角枚舉Enum。
枚舉類型的對應值
你調用時傳一個1,也會輸出我要去spa。
const result = getServe(1); 復制代碼這看起來很神奇,這是因為枚舉類型是有對應的數字值的,默認是從 0 開始的。我們直接用console.log()就可以看出來了。
console.log(Status.MASSAGE); console.log(Status.SPA); console.log(Status.DABAOJIAN); 復制代碼可以看出結果就是0,1,2。那這時候不想默認從 0 開始,而是想從 1 開始。可以這樣寫。
enum Status {MASSAGE = 1,SPA,DABAOJIAN, }復制代碼枚舉通過下標反查
我們這里能打印出枚舉的值(也有叫下標的),那如果我們知道下標后,也可以通過反差的方法,得到枚舉的值。
console.log(Status.MASSAGE, Status[1]); 復制代碼這樣就進行了反查。
20. TypeScript 函數泛型-難點
泛型我個人認為是 TypeScript 利的一個難點,我第一次學完后根本不能完全理解,所以從這節課開始,我們應該算是一個進階教程了,難度也開始上來了,如果你一遍聽不太明白,可以反復聽幾次,然后多做練習。
編寫一個聯合類型 Demo
現在跟著我作一個簡單的join方法,方法接受兩個參數first和second,參數有可能是字符串類型,也有可能是數字類型。方法里為了保證都可以使用,所以我們只作了字符串的基本拼接。
function join(first: string | number, second: string | number) {return `${first}${second}`; } join("jspang", ".com"); 復制代碼這個方法現在沒有任何問題,但現在有這樣一個需求,就是first參數如果傳的是字符串類型,要求second也傳字符串類型.同理,如果是number類型,就都是number類型。
那現在所學的知識就完成不了啦,所以需要學習泛型來解決這個問題。
初始泛型概念-generic
泛型:[generic - 通用、泛指的意思],那最簡單的理解,泛型就是泛指的類型。
泛型的定義使用<>(尖角號)進行定義的,比如現在給join方法一個泛型,名字就叫做JSPang(起這個名字的意思,就是你可以隨便起一個名字,但工作中要進行語義化。),后邊的參數,這時候他也使用剛定義的泛型名稱。然后在正式調用這個方法時,就需要具體指明泛型的類型啦。
function join<JSPang>(first: JSPang, second: JSPang) {return `${first}${second}`; } join < string > ("jspang", ".com"); 復制代碼如果要是number類型,就直接在調用方法的時候進行更改就可以了。
join < number > (1, 2); 復制代碼這就是最簡單的泛型理解,因為在實際開發中,有很多對象和類的情況,里邊的具體類型我們沒辦法了解,所以提供了這種泛型的概念。
泛型中數組的使用
如果傳遞過來的值要求是數字,如何用泛型進行定義那?兩種方法,第一種是直接使用[],第二種是使用Array<泛型>。形式不一樣,其他的都一樣。
第一種寫法:
function myFun<ANY>(params: ANY[]) {return params; } myFun < string > ["123", "456"]; 復制代碼第二種寫法:
function myFun<ANY>(params: Array<ANY>) {return params; } myFun < string > ["123", "456"]; 復制代碼在工作中,我們經常使用``來作泛型的表示,當然這不是硬性的規定,只是大部分程序員的習慣性寫法。
多個泛型的定義
一個函數只能定義一個泛型嗎?當然不是,是可以定義多個的,這里還是拿join方法舉例,定義多個泛型,比如第一個泛型用T,第二個用P代表。
function join<T, P>(first: T, second: P) {return `${first}${second}`; } join < number, string > (1, "2"); 復制代碼會了兩種,你也就會了三種以上,泛型在造輪子的時候經常使用,因為造輪子很多東西都需要靈活性。泛型給了我們很好的靈活性。需要注意的是,如果函數定義了多個泛型,使用時要對應的定義出具體的類型。
泛型的類型推斷
泛型也是支持類型推斷的,比如下面的代碼并沒有報錯,這就是類型推斷的功勞。
function join<T, P>(first: T, second: P) {return `${first}${second}`; } join(1, "2"); 復制代碼但個人不建議大量使用類型推斷,這會讓你的代碼易讀和健壯性都會下降,所以這個知識點,大家做一個了解就可以了。
好了,這就是這節課的內容了,希望小伙伴們一定要練習一下,加深理解。
21. TypeScript 類中泛型-難點
上節課學習了在函數(方法)中使用泛型的基本語法,這節課在看看類中泛型的使用方法。
編寫一個基本類
為了下面的教學演示,所以我先編寫一個基本的類SelectGirl,在類的構造函數中(constructor)需要傳遞一組女孩的名稱,然后再通過下邊展現女孩的名稱,代碼如下:
class SelectGirl {constructor(private girls: string[]) {}getGirl(index: number): string {return this.girls[index];} }const selectGirl = new SelectGirl(["大腳", "劉英", "曉紅"]); console.log(selectGirl.getGirl(1)); 復制代碼寫完后,我們可以在終端中使用ts-node Demo.ts進行預覽,可以看到控制臺中輸出了劉英的名字。學到現在你寫這樣的一個類應該是非常容易的了。
現在問題來了,比如現在更好的保護小姐姐,這些小姐姐使用編號啦,那我們程序要如何修改。需要寫成下面的樣子,這時候我們代碼看起來就沒有那么優雅了,在 TypeScript 中,編寫復雜代碼的時候,會經常使用泛型。
class SelectGirl {constructor(private girls: string[] | number[]) {}getGirl(index: number): string | number {return this.girls[index];} } 復制代碼初始類的泛型
這時候我們要用泛型重構代碼,要如何作那?有了上節課的基礎,應該很好理解,就是用<>編寫,我們把代碼修改成了這個樣子。
class SelectGirl<T> {constructor(private girls: T[]) {}getGirl(index: number): T {return this.girls[index];} }const selectGirl = new SelectGirl(["大腳", "劉英", "曉紅"]); console.log(selectGirl.getGirl(1));復制代碼這時候代碼并不報錯,也使用了泛型,但是在實例化對象的時候,TypeScript 是通過類型推斷出來的。上節課已經介紹,這種方法并不好,所以還是需要在實例化對象的時候,對泛型的值進行確定,比如是string類型,就這樣寫。
const selectGirl = new SelectGirl() < string > ["大腳", "劉英", "曉紅"]; 復制代碼這就是類里邊最基礎的泛型使用了,如果你還不理解,請現在敲出上面的例子進行練習,不要繼續學習了。
泛型中的繼承
現在需求又變了,要求返回是一個對象中的name,也就是下面的代碼要改成這個樣子。
return this.girls[index].name; 復制代碼現在的代碼一定時報錯的,但是這時候還要求我們這么做,意思就是說傳遞過來的值必須是一個對象類型的,里邊還要有name屬性。這時候就要用到繼承了,我用接口的方式來實現。寫一個Girl的接口,每個接口里都要有 name 屬性。代碼如下:
interface Girl {name: string; } 復制代碼有了接口后用extends關鍵字實現泛型繼承,代碼如下:
class SelectGirl<T extends Girl> {... } 復制代碼這句代碼的意思是泛型里必須有一個name屬性,因為它繼承了Girl接口。
現在程序還是報錯的,因為我們getGirl方法的返回類型還不對,這時候應該是一個string類型才對,所以代碼應該改為下面的樣子:
interface Girl {name: string; }class SelectGirl<T extends Girl> {constructor(private girls: T[]) {}getGirl(index: number): string {return this.girls[index].name;} }const selectGirl = new SelectGirl([{ name: "大腳" },{ name: "劉英" },{ name: "曉紅" }, ]); console.log(selectGirl.getGirl(1));復制代碼我們回過頭來看一下這段代碼的意思,就是我們在SelectGirl類中使用了泛型,意思是我不知道我以后要用什么類型,但是我有一個約束條件,這個類型,必須要有一個name屬性。這個在工作中經常使用,所以必須要好好理解這的知識。 初學泛型肯定會很難理解,我當時看書也是看的一臉懵,經過反復的實驗和看別人的源代碼,才對泛型有了比較深的理解。
泛型約束
現在的泛型可以是任意類型,可以是對象、字符串、布爾、數字都是可以的。但你現在要求這個泛型必須是string或者number類型。我們還是拿上面的例子,不過把代碼改為最初的樣子。
class SelectGirl<T> {constructor(private girls: T[]) {}getGirl(index: number): T {return this.girls[index];} }const selectGirl = new SelectGirl<string>(["大腳", "劉英", "曉紅"]); console.log(selectGirl.getGirl(1));復制代碼然后進行約束,這時候還是可以使用關鍵字extends來進行約束,把代碼改成下面的樣子。
class SelectGirl<T extends number | string> {//..... }復制代碼作為教學泛型講這些就可以了,但是在實際工作中,泛型的應用更廣泛和復雜,這些需要在實際項目中不斷精進和加深理解,有句話說的非常好,師傅領進門,修行在個人了。
22. 初識命名空間-Namespace
以前的課程都是通過Node來運行代碼的,這節課為了有更好的演示效果,我們要在瀏覽器中運行代碼。這就要求我們重新創建一個項目,直接在桌面上建立一個文件夾TSWeb。
搭建瀏覽器開發環境步驟
已經有好幾個小伙伴通過 QQ 問我如何搭建一個最基礎的 TS 開發環境了。正好這節課也需要,我們就從新搭建一下。如果你已經很熟悉這部分內容可以跳過。
這就是你開發最基礎的前端項目時需要作的環境配置。我覺的學習這東西,學會了就要用,如果不用你很快就會忘記。所以以后你在做項目,請盡量使用TypeScript來進行編寫。
沒有命名空間時的問題
為了你更好的理解,先寫一下這樣代碼,用類的形式在index.html中實現header,content和Footer部分,類似我們常說的模板。
在page.ts文件里,寫出下面的代碼:
class Header {constructor() {const elem = document.createElement("div");elem.innerText = "This is Header";document.body.appendChild(elem);} }class Content {constructor() {const elem = document.createElement("div");elem.innerText = "This is Content";document.body.appendChild(elem);} }class Footer {constructor() {const elem = document.createElement("div");elem.innerText = "This is Footer";document.body.appendChild(elem);} }class Page {constructor() {new Header();new Content();new Footer();} } 復制代碼寫完后我們用tsc進行編譯一次,然后修改index.html文件,在標簽里引入標簽,并實例化Page,代碼如下:
<body><script>new Page();</script> </body> 復制代碼這時候再到瀏覽器進行預覽,就可以看到對應的頁面被展現出來了。看起來沒有什么問題,但是有經驗的程序員就會發現,這樣寫全部都是全局變量(通過查看./build/page.js文件可以看出全部都是var聲明的變量)。過多的全局變量會讓我們代碼變的不可維護。
這時候你在瀏覽器的控制臺(Console)中,分別輸入Header、Content、Footer和Page都時可以拿到對應的變量的,說明他們全都是全局變量。
其實你理想的是,只要有Page這個全局變量就足夠了,剩下的可以模塊化封裝起來,不暴露到全局。
命名空間的使用
命名空間這個語法,很類似編程中常說的模塊化思想,比如webpack打包時,每個模塊有自己的環境,不會污染其他模塊,不會有全局變量產生。命名空間就跟這個很類似,注意這里是類似,而不是相同。
命名空間聲明的關鍵詞是namespace 比如聲明一個namespace Home,需要暴露出去的類,可以使用export關鍵詞,這樣只有暴漏出去的類是全局的,其他的不會再生成全局污染了。修改后的代碼如下:
namespace Home {class Header {constructor() {const elem = document.createElement("div");elem.innerText = "This is Header";document.body.appendChild(elem);}}class Content {constructor() {const elem = document.createElement("div");elem.innerText = "This is Content";document.body.appendChild(elem);}}class Footer {constructor() {const elem = document.createElement("div");elem.innerText = "This is Footer";document.body.appendChild(elem);}}export class Page {constructor() {new Header();new Content();new Footer();}} } 復制代碼TS 代碼寫完后,再到index.html文件中進行修改,用命名空間的形式進行調用,就可以正常了。 寫完后,記得用tsc編譯一下,當然你也可以使用tsc -w進行監視了,只要有改變就會進行重新編譯。
new Home.Page(); 復制代碼現在再到瀏覽器中進行查看,可以看到現在就只有Home.Page是在控制臺可以得到的,其他的Home.Header…這些都是得不到的,說明只有Home.Page是全局的,其他的都是模塊化私有的。
這就是 TypeScript 給我們提供的類似模塊化開發的語法,它的好處就是讓全局變量減少了很多,實現了基本的封裝,減少了全局變量的污染。
23. 深入命名空間-Namespace
接著上節課的內容進行學習,廢話不多說,直接開講。
用命名空間實現組件化
上節課的代碼雖實現了模塊化和全局變量的污染,但是我們工作中分的要更細致一些,會單獨寫一個components的文件,然后進行組件化。
在src目錄下新建一個文件components.ts,編寫代碼如下:
namespace Components {export class Header {constructor() {const elem = document.createElement("div");elem.innerText = "This is Header";document.body.appendChild(elem);}}export class Content {constructor() {const elem = document.createElement("div");elem.innerText = "This is Content";document.body.appendChild(elem);}}export class Footer {constructor() {const elem = document.createElement("div");elem.innerText = "This is Footer";document.body.appendChild(elem);}} }復制代碼這里需要注意的是,我每個類(class)都使用了export導出,導出后就可以在page.ts中使用這些組件了。比如這樣使用-代碼如下。
namespace Home {export class Page {constructor() {new Components.Header();new Components.Content();new Components.Footer();}} }復制代碼這時候你可以使用tsc進行重新編譯,但在預覽時,你會發現還是會報錯,找不到Components,想解決這個問題,我們必須要在index.html里進行引入components.js文件。
<script src="./build/page.js"></script> <script src="./build/components.js"></script> 復制代碼這樣才可以正常的出現效果。但這樣引入太麻煩了,可不可以像webpack一樣,只生成一個文件那?那答案是肯定的。
多文件編譯成一個文件
直接打開tsconfig.json文件,然后找到outFile配置項,這個就是用來生成一個文件的設置,但是如果設置了它,就不再支持"module":"commonjs"設置了,我們需要把它改成"module":"amd",然后在去掉對應的outFile注釋,設置成下面的樣子。
{"outFile": "./build/page.js" } 復制代碼配置好后,刪除掉build下的js文件,然后用tsc進行再次編譯。
然后刪掉index.html文件中的component.js,在瀏覽器里還是可以正常運行的。
子命名空間
也就是說在命名空間里,再寫一個命名空間,比如在Components.ts文件下修改代碼如下。
namespace Components {export namespace SubComponents {export class Test {}}//someting ... }復制代碼寫完后在控制臺再次編輯tsc,然后你在瀏覽器中也是可以查到這個命名空間的Components.SubComponents.Test(需要刷新頁面后才會顯示)。
通過兩節課的時間,基本講完了命名空間的內容,在工作中如果遇到,這些知識已經完全夠用,所以這部分內容就先到這里了。
24. TypeScript 如何使用 import 語法
上節我們學習了命名空間的知識,有些小伙伴就有疑問了,說我看別人寫的代碼,都是用import進行引入的,你這個顯得有點不專業哦。那這節我們就把上節的代碼改成import引入。
修改 components.ts 文件
現在去掉components.ts里的namespace命名空間代碼,寫成 ES6 的 export 導出模式。代碼如下:
export class Header {constructor() {const elem = document.createElement("div");elem.innerText = "This is Header";document.body.appendChild(elem);} }export class Content {constructor() {const elem = document.createElement("div");elem.innerText = "This is Content";document.body.appendChild(elem);} }export class Footer {constructor() {const elem = document.createElement("div");elem.innerText = "This is Footer";document.body.appendChild(elem);} } 復制代碼現在三個類就都已經用export導出了,也就是說可以實現用import進行引入了。
修改 page.ts 文件
來到page.ts文件,去掉namespace命名空間對應的代碼,然后使用 import 語法進行導入Header、Content和Footer,代碼如下:
import { Header, Content, Footer } from "./components"; export class Page {constructor() {new Header();new Content();new Footer();} } 復制代碼現在看起來確實和工作中寫的代碼非常類似了。這時候可以使用tsc進行編譯。然后可以看到編譯好的代碼都是define開頭的(這是 amd 規范的代碼,不能直接在瀏覽器中運行,可以在 Node 中直接運行),這種代碼在瀏覽器中是沒辦法被直接運行的,需要其他庫(require.js)的支持。
引入 require.js
我這里使用了一個現成的 CDN 的require.js,地址你可以直接復制,然后用``標簽進行引入。
Require.js 的 CDN 地址: cdnjs.cloudflare.com/ajax/libs/r…
復制好 URL 地址后,記得使用``標簽進行引入,代碼如下。
<script src="https://cdnjs.cloudflare.com/ajax/libs/require.js/2.3.6/require.js"></script> 復制代碼這時候就可以解析define這樣的語法了。然后把page.ts中加入default關鍵字,如果不加是沒辦法直接引用到的。
import { Header, Content, Footer } from "./components";export default class Page {constructor() {new Header();new Content();new Footer();} } 復制代碼這時候再用tsc進行編譯一下,你會發現還是又問題。因為使用export default這種形式的語法,需要在html里用require來進行引入。
require 方式引入
因為你已經加入了require.js這個庫,所以現在可以直接在代碼中使用require了。具體代碼如下:
<body><script>require(["page"], function (page) {new page.default();});</script> </body> 復制代碼寫完這部,刷新頁面,可以看到正常顯示出來了,雖然用起來比較麻煩,但是我們還是實現了用import來進行引入,當我們又了webpack和Parcel的時候就不會這么麻煩,這些都交給打包工具來處理就好了。
25. 用 Parcel 打包 TypeScript 代碼
上節課代碼配置起來非常麻煩,步驟也很多。工作中一定是有更好的解決方案的。其實最通用的有兩種解決方案Webpack和Parcel。webpack不用多說,只要是前端基本都會,這幾年Parcel也開始崛起,用的人也越來越多。所以這節課就講一下,如何使用Parcel來打包TypeScript代碼。
建立一個新項目
這里給出新建項目的步驟,如果你已經熟悉此過程,可以跳過。
index.html 文件代碼:
<!DOCTYPE html> <html lang="en"><head><meta charset="UTF-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><script src="./page.ts"></script><title>Document</title></head><body></body> </html>復制代碼page.ts 文件代碼:
const teacher: string = "jspang"; console.log(teacher); 復制代碼這時候我們并不能正常的預覽出效果,我們需要Parcel的幫忙。
Parcel 的安裝和使用
Parcel 可以通過npm或者yarn來進行安裝,我這里npm安裝很慢,會 5 分鐘左右,所以我使用yarn來進行安裝。代碼如下。
yarn add --dev parcel@next 復制代碼使用 yarn 安裝大概需要 1 分鐘左右,這些主要看你自身的網絡情況。
安裝好以后,打開package.json文件,可以看到這樣一段代碼,我安裝的版本是^2.0.0-beta.1,如果你學習時和這個版本不一樣,操作可能會稍有不同。
修改package.json里邊的代碼。
{"scripts": {"test": "parcel ./src/index.html"}, } 復制代碼這個意思就是使用parcel對index.html進行一個編譯。
然后打開終端輸入yarn test,這時候終端會給出一個地址http://localhost:1234,把地址放到瀏覽器上,可以看到瀏覽器的控制臺會輸出jspang。
這說明Parcel會自動對index.html中引入的TypeScript文件進行編譯,然后打包好后,就可以直接使用了。
使用Parcel大大簡化了我們的配置過程,如果你想詳細學習Parcel可以自行學習,畢竟我們這個是 TypeScript 的教程,所以更多的 Parcel 知識就不作介紹了。
26. 在 TypeScript 中使用 JQuery
這個需求也經常使用,就是在 TypeScript 的代碼中使用其他類庫,其實這里就涉及到一個類型文件(Type file)的問題,網上有大量別人寫好的類型文件,我們只要下載使用就可以了。
引入 JQuery 框架庫
接著上節課的代碼,在TSTest文件夾的src目錄下,引入JQuery文件,這里采用CDN的形式進行引入。
BootCDN 地址: cdn.bootcdn.net/ajax/libs/j…
直接在index.html加入``標簽,代碼如下:
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.js"></script> 復制代碼有了 jquery 框架,就可以在TypeScript文件中進行使用JQuery的語法了。
然后在page.ts文件中編寫如下代碼。
const teacher: string = "jspang"; console.log(teacher);$(function () {alert("jspang"); }); 復制代碼寫完后到終端中輸入yarn test進行編譯和啟動服務。然后在地址欄輸入了http://localhost:1234,可以看到程序可以正常輸出,也沒有任何的報錯。
安裝 types/jquery(解決方法)
第一種:就是安裝別人寫好的文件
但是在vscode中是會報錯的,這時候就需要我們安裝類型文件type file,直接可以用 npm 進行安裝。
npm i @types/jquery 復制代碼這個安裝的時間還是比較長的,所以視頻中我就不進行展示了。
第二種:簡單粗暴
還有一種簡單粗暴的方法的方式就是直接在page.ts文件的頭部加入這句代碼:
declare var $: any; 復制代碼第三種:自己寫一個.d.ts聲明文件的類庫,如果你用的類庫很少見,就需要自己寫了。這個寫起來還是很麻煩的。我只是簡單的學過,但在工作中從來沒自己寫過,所以也不推薦給大家。比如 JQuery 就有幾十個接口,如果你要寫,這個文件會寫很長,所以原則就是有別人寫好的就直接用,實在沒有就用粗暴的方法,如果實在不行,再考慮寫.d.ts聲明文件。
TypeScrip的視頻我們就暫時告一段落,緊接著會更新Vue3的課程,希望下伙伴們繼續跟我一期學習。
總結
以上是生活随笔為你收集整理的TypeScript免费视频图文教程(2W字)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python 中阶乘怎么打_阶乘pyth
- 下一篇: 使用Python模拟武侠小说中两派人的一