TS 準備
首先我們準備一個目錄,使用 dos 進入到某目錄,當然你直接 vs 打開終端執行也是沒有問題的:
執行以下命令安裝 typescrip(不用進入目錄,直接安裝即可):
npm install -g typescript
我是已經安裝過了:
接著你可以初始化項目(這里需要進入文件夾了,畢竟是你的項目目錄對吧,初始化項目就是以當前目錄默認初始化):
tsc --init
此時你的目錄中會出現一個 tsconfig 文件:
里面存儲的是一些 ts 的config ,暫時不用理,接下來就可以開始 ts 的編程了。
一、TS 編譯
安裝好 nodejs 之后,新建一個 01.ts 文件:
編寫代碼:
console.log("HI")
由于 ts 最終將會編譯生成 js,你在 ts 中直接寫 js 是沒有問題的(并且ts不是一個全新的語言,是基于 js 的),在終端中輸入 node 01.ts:
此時將會輸出 HI~。
二、變量定義及編譯
在 ts 變量定中需要指定的這個變量類型,這個是跟 js 大有不同,又或者說這該指定類型貫穿了整個 ts。
更改 01 代碼:
let age: number = 18;
console.log(age)
以上代碼中定義了一個 age變量,類型為 number,賦值為 18,此時在變量名之后有一個冒號,冒號右側就是對應的這個變量的類型,最終使用等于號賦值為 18。
此時由于我們私用的是 ts 的語法,你直接使用 node 話會錯誤,需要使用 tsc-node 命令對 ts 文件進行編譯:
若你使用 node 命令就不是編譯了,此時將會報錯:
使用 node 表示運行 js 文件,而不是 編譯 ts 文件。
在創建一個變量的時候,也可以不指定類型,直接給定一個值,這個時候 ts 會完成類型的自動推導(跟go類似),這樣就知道這個變量是什么值了,例如:
let age = 11;
三、生成 js 文件
在 ts 中,還可以直接使用 tsc 對 ts 編譯生成 js 文件:
執行命令后,將會在當前目錄下生成一個 js 文件,并且這個 js 文件是對應 ts 的 js 代碼:
在此我們一定要注意, ts 你可以理解為是一種強制性的“規范語法”框架,在該“語法框架”下需要嚴謹的對某些動作進行處理,但最終的本質還是 js。
四、變量、數組
4.1 一般數據類型
ts 中類型有 number、string、boolean、symbol、數組、map 等,在此只介紹常見類型。
以下是對應這些類型的示例:
let age: number = 18;
let authorName: string = '1_bit';
let boolVal: boolean = true;//數組
let strs: string[] = ["1_bit", "blog", "author"];
let strOrNumber: (string | number)[] = ["1_bit", 11, "blog", "author", 22];
以上的示例中,主要看數組的定義,例如 let strs: string[] = ["1_bit", "blog", "author"];
,在這段代碼中 strs 是數組名,冒號后就是對應的類型約束,在此是 string,而方括號 “ [] ” 則表示這是一個數組,在等于號右側則是這個數組中的值。
已經理解了基本的數組的 ts 數組,在查看對應的 strOrNumber 數組,在這個代碼中,用圓括號包裹了 string 和 number 并且中間使用了“或”運算的 “ | ” 鏈接,這是表示這個數組中可以存儲 string 以及 number 類型,所以在數組中可以看到 string 和 number 類型的數據都存在。
4.2 元組
學過 python 的小伙伴對元組應該很熟悉,在 ts 中的元組跟 python 中的不是很一樣,在 ts 中定義的元組你可以理解為固長、固定類型的數組,可以指定某個位置的類型以及指定整個數組的長度,以下為元組示例:
let postion: [number, number];
postion = [0.45, 0.50];console.log(postion)
console.log(postion[0])
以上代碼中 let postion: [number, number];
為定義了一個元組,但是并沒有進行初始化,接下來直接給 postion 定義了一個值[0.45, 0.50]
,隨后使用 console.log 對其打印。
在此我們可以看到,數組的定義方式是之直接在變量的冒號后面使用一個類型加方括號“[]”定義一個數組,在元組中則是在方括號內編寫對應的數值類型。
運行后結果如下:
4.3 any
在 ts 中不推薦使用 any 類型,你用了不就等于跟原本的 js 沒啥區別了嗎?若真的有必要使用 any 推薦臨時使用,別貫穿項目。
以下是any 的錯誤示范(語法正確,使用錯誤):
let age: any;
age = "str";
console.log(age)
console.log(typeof age)
結果如下:
五、自定義類型(別名)
在 ts 中,若一個數組的類型經常使用,可以定義一個類型別名,在使用這個類型時即可簡短的通過這個別名表示這個類型:
type StuT = (string | number)[]
let stus: StuT = ["1_bit", 13, "Xi", 26]
以上示例定義了一個 type 類型,這個類型為 (string | number)[]
表示是一個字符串與數組的數組類型,接著在下一行代碼中直接創建了一個 stus 變量,指定為 StuT 類型,并且賦值有字符串和數字的值。
六、函數
6.1 基本函數
ts 語言中的自定義函數跟 go 中的有點相似,例如如下是一個 ts 中的自定義函數:
function sayHi(name: string, age: number): string {return "Hi " + name + " you " + age + " age";
}
let xiName: string = "Xi";
let xiAge: number = 19;
console.log(sayHi(xiName, xiAge));
在以上示例中 sayHi 是一個自定義函數,跟js 語法一樣,使用function 定義一個函數,在 sayHi 函數中有兩個參數,一個是 name 還有一個是 age,name 參數是一個 string 類型,age 是一個 number 類型,參數的聲明在此也是不同的,使用了 ts 的語法;還有一點不同的是在參數之后使用一個冒號說明了當前函數的返回值為一個 string,在此需要注意,這是 ts 的語法。
接著我創建了兩個邊路 xiName 以及 xiAge,用此當做參數傳入到 sayHi 之中,此時使用 ts-node 命令編譯運行我們的 ts 文件:
6.2 箭頭函數
函數的編寫還有另外一種使用箭頭函數的方式:
const sayHi = (name: string, age: number): string => {return "Hi " + name + " you " + age + " age";
}
let xiName: string = "Xi";
let xiAge: number = 19;
console.log(sayHi(xiName, xiAge));
6.3 箭頭函數加強版
函數還有另外一種定義方式,可能大家都不是特別想去學習:
const sayHi: (name: string, age: number) => string = (name, age) => {return "Hi " + name + " you " + age + " age";
}
其實這個形式可以看成兩個部分:
其中前半部分是為這個 sayHi 聲明一個類型,這個 sayHi 的類型是 (name: string, age: number)
由于需要指定這個函數返回值,所以使用 => string
表示當前函數返回值為 string。
接著我們再看第二個部分:
這一部分就直接當做函數的內容即可,而 (name, age)
表示這個函數接收兩個參數,把之前所說明的 name 和 age 兩個變量傳入到函數,所以在此處并不用使用類型對其進行約束,在之前已經做好約束了,最后箭頭函數右側則是函數體。
函數返回值若為空則使用 void。
6.4 可選參數
可選參數在 ts 中表示這個參數可傳或不傳,非必須參數,例如我們現在修改以上示例完成需求“姓名必傳、年齡和身高為可選,傳入年齡或身高需要對應的對其進行顯示”。
若完成這個需求那么必然會有一個函數對某個值進行判斷,此時做一個檢測的函數:
function isUndefine(arg: (number | string | undefined)): boolean {let check: boolean = true;if (!arg) {check = false;}return check;
}
以上檢測 agr 參數,參數因為需要時 number、string、undefined 類型的其中一個,所以在此處設定了這個參數的類型范圍,接著有一個 Boolean 的返回值,在函數體中給了一個變量 check 為 Boolean,初始值為 true,只要 arg 不存在那么久 fase 即可,最后返回 check。
接著我們修改 sayHi 函數,修改后調用檢測參數的函數,完成內容的拼接:
const sayHi: (name: string, age?: number, height?: number) => string = (name, age, height) => {let sayStr = 'Hi ' + name;if (isUndefine(age)) {sayStr += " you " + age + " age";}if (isUndefine(height)) {sayStr += " you " + age + " age " + "height " + height;}return sayStr;
}
此時兩個 if 判斷 age 或者 height 是否存在,隨后進行拼接即可,最后返回了sayStr 這個字符串。
在這里我們可以看到,參數 age、height 在參數名后添加了一個問號,這個問號就是表示可選參數。
最終完整代碼如下:
function isUndefine(arg: (number | string | undefined)): boolean {let check: boolean = true;if (!arg) {check = false;}return check;
}const sayHi: (name: string, age?: number, height?: number) => string = (name, age, height) => {let sayStr = 'Hi ' + name;if (isUndefine(age)) {sayStr += " you " + age + " age";}if (isUndefine(height)) {sayStr += " you " + age + " age " + "height " + height;}return sayStr;
}
let xiName: string = "Xi";
let xiAge: number = 19;
console.log(sayHi(xiName, xiAge, 170));
最終傳入不同的參數將會顯示不同的結果:
七、對象
在 ts 中使用對象分為聲明和定義,聲明就像類型一樣進行使用,定義則是賦予這個對象的值:
let stu: {name: string;age: number;height: number;getName(): string;getAge(): number;setName(name: string): void;setAge(age: number): void;} = {name: 'Xi',age: 11,height: 170,setName(name) {this.name = name;},setAge(age) {this.age},getName() {return this.name;},getAge() {return this.age},
}stu.setName("1_bit")
console.log(stu.getName())
以上實例中定義了一個 stu 的對象,其中包含 name、age、height 變量以及 getName 等方法,這些都可以看做是這個對象的“類型”,隨后賦值為這些“類型”的初始化,并且設定函數的實現。
在此一定要注意,你既然聲明了,那就必須要實現(若刪除一個成員變量的初始化),否則會報錯:
運行結果如下:
八、類
8.1 類的基本使用
在 ts 中定義一個類方式很簡單,使用 class 例如如下示例:
class Stu {public readonly name: string;public age: number;private nickname: string = '';private readonly color = 'yellow';constructor(_name: string, _age: number) {this.name = _name;this.age = _age;}public setNickName(_nick: string) {this.nickname = _nick;}public getNickName() {return this.nickname;}
}
以上示例中使用 class 創建了一個 stu 類,在這個類中定義了幾個成員變量,在這幾個成員變量中使用了 public、private 對其進行修飾,public 表公開都可以調用,private 私有,自由類內部可以調用,除了這些常規的修飾之外還有一個 readonly,readonly 表示當前字段不允許修改,若你想對其進行改動那么將會報錯:
在此還需要注意,這些變量你都需要對其進行初始化,否則將會報錯。
在類中還有一個構造方法,ts 是支持構造方法的;在使用成員變量時需要使用 this 對其指向,直接使用變量名將會出錯。
ts冷笑話:在 class 中創建“函數”不需要使用 function,因為他是方法。
接著創建一個對象:
const XiaoMing = new Stu("XiaoMing", 18);
XiaoMing.setNickName("MM");
console.log(XiaoMing.getNickName());
接著使用 tc 命令編譯運行:
在 ts 中類的繼承使用 extends:
class Stu1 extends Stu {public getName(): string {return this.name}
}
繼承后在 Stu1將會擁有 Stu 的成員變量和方法,使用和正常一個對象使用一致:
const XiaoMing1 = new Stu1("XiaoM", 18);
console.log(XiaoMing1.getName());
8.2 函數重載
8.3 類的兼容性
以下是一個示例,在 ts 中相同成員變量的類可以互相兼容:
class Stu1 {name: string;height: number;constructor() {this.name = "xiaoM";this.height = 170;}
}class Stu2 {name: string;height: number;constructor() {this.name = "xiaoY";this.height = 160;}
}const xiaoY: Stu1 = new Stu2()
若此時 Stu2 中多了一個成員變量呢:
此時 Stu1 還是兼容于 Stu2 的,少的可以兼容于多的,若反過來是不可以的:
當然多個方法也沒問題:
九、接口
9.1 接口的一般使用
在 ts 中支持接口,使用關鍵字 interface:
interface stuI {height: number;name: string;getName(): string;
}
以上定義了一個接口,若需要某些變量為該接口類型,那么就需要對接口中的變量進行初始化和實現方法:
let stu: stuI = {height: 170,name: "1_bit",getName() {return this.name;}
}
實現方法很簡單,就是 let 一個變量后指定接口為類型,隨后使用花括號對其中的內容進行初始化。
接口還可以繼承,例如如下示例:
interface stuI1 extends stuI {color: string,getHeight(): number
}
以上 stuI1 繼承 stuI,并且繼承了其中的內容,在 stuI1 接口中可以為接口本身編寫 stuI1 的內容。
使用也如下使用示例(需要完全實現接口、父接口的中的內容):
let stu1: stuI1 = {height: 170,name: "1_bit",color: "yello",getName() {return this.name;},getHeight() {return this.height}
}
最后調用:
console.log(stu.getName());
console.log(stu1.getHeight());
9.2 交叉類型與同名成員變量
交叉類型其實就是多個接口進行合并,如下示例:
interface BBB { name: string }
interface CCC { age: number }
interface DDD { height: number }type BCD = BBB & CCC & DDD
let val: BCD = {name: 'ccc',age: 123,height: 12121
}
交叉類型直接使用 & 對某個類型進行定義即可,之后的變量在使用這個類型的時候就必須實現這些接口中的內容。
若接口中有同名,那么那么變量就是 never 類型,表示不可能,永遠不可能。
9.2 交叉類型與同名函數參數
在同名函數中的參數同名但類型不同的情況下可以理解為同時兼容于兩個類型,例如:
interface CCC {age: number;getVl(key: number): string
}
interface DDD {height: number;getVl(key: string): string
}
以上兩個接口新增了一個同名方法 getVl,參數同名卻參數類型不同,此時就可以當成是 getVl(key: (number | string))
,例如如下代碼:
type BCD = BBB & CCC & DDD
let val: BCD = {name: 'ccc',age: 123,height: 12121,getVl(key: (number | string)) {console.log(key)return "1";}
}val.getVl(233);