TypeScript上手指南
前言
?哈嘍,我是樹醬,今天分享一篇蛙人的typescript指南,自從vue3、還有社區(qū)一些開源工具、組件庫等都開始基于typescript開發(fā),其自帶的"可選的靜態(tài)類型系統(tǒng)",可以使得當我們在應用程序運行之前,通過編譯器就可以顯示有關任何潛在問題的警告
?
一、為什么要用TypeScript
TypeScript可以讓我們開發(fā)中避免一些類型或者一些不是我們預期希望的代碼結(jié)果錯誤。xxx is not defined 我們都知道JavaScript錯誤是在運行中才拋出的,但是TypeScript錯誤直接是在編輯器里告知我們的,這極大的提升了開發(fā)效率,也不用花大量的時間去寫單測,同時也避免了大量的時間排查Bug。
二、TypeScript優(yōu)缺點
優(yōu)點
一般我們在前后端聯(lián)調(diào)時,都要去看接口文檔上的字段類型,而
TypeScript會自動幫我們識別當前的類型。節(jié)省了我們?nèi)タ?code style="overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;">文檔或者network時間。這叫做類型推導(待會我們會講到)友好地在編輯器里提示錯誤,避免代碼在運行時類型隱式轉(zhuǎn)換踩坑。
缺點
有一定的學習成本,
TypeScript中有幾種類型概念,interface接口、class類、enum枚舉、generics泛型等這些需要我們花時間學習。可能和一些插件庫結(jié)合的不是很完美
三、TypeScript運行流程及JavaScript代碼運行流程
「1. JavaScript運行流程如下,依賴NodeJs環(huán)境和瀏覽器環(huán)境」
將 JavaScript代碼轉(zhuǎn)換為JavaScript-AST將 AST代碼轉(zhuǎn)換為字節(jié)碼運算時計算字節(jié)碼
「2. TypeScript運行流程,以下操作均為TSC操作,三步執(zhí)行完繼續(xù)同上操作,讓瀏覽器解析」
將 TypeScript代碼編譯為TypeScript-AST檢查 AST代碼上類型檢查類型檢查后,編譯為 JavaScript代碼JavaScript代碼轉(zhuǎn)換為JavaScript-AST將 AST代碼轉(zhuǎn)換為字節(jié)碼運算時計算字節(jié)碼
四、TypeScript和JavaScript區(qū)別
只有搞懂了二者的區(qū)別,我們才可以更好的理解TypeScript
| 類型系統(tǒng)特性 | JavaScript | TypeScript |
|---|---|---|
| 類型是如何綁定? | 動態(tài) | 靜態(tài) |
| 是否存在類型隱式轉(zhuǎn)換? | 是 | 否 |
| 何時檢查類型? | 運行時 | 編譯時 |
| 何時報告錯誤 | 運行時 | 編譯時 |
類型綁定
「JavaScript」
JavaScript動態(tài)綁定類型,只有運行程序才能知道類型,在程序運行之前JavaScript對類型一無所知
「TypeScript」
TypeScript是在程序運行前(也就是編譯時)就會知道當前是什么類型。當然如果該變量沒有定義類型,那么TypeScript會自動類型推導出來。
類型轉(zhuǎn)換
「JavaScript」
比如在JavaScript中1 + true這樣一個代碼片段,JavaScript存在隱式轉(zhuǎn)換,這時true會變成number類型number(true)和1相加。
「TypeScript」
在TypeScript中,1+true這樣的代碼會在TypeScript中報錯,提示number類型不能和boolean類型進行運算。
何時檢查類型
「JavaScript」
在JavaScript中只有在程序運行時才能檢查類型。類型也會存在隱式轉(zhuǎn)換,很坑。
「TypeScript」
在TypeScript中,在編譯時就會檢查類型,如果和預期的類型不符合直接會在編輯器里報錯、爆紅
何時報告錯誤
「JavaScript」
在JavaScript只有在程序執(zhí)行時才能拋出異常,JavaScript存在隱式轉(zhuǎn)換,等我們程序執(zhí)行時才能真正的知道代碼類型是否是預期的類型,代碼是不是有效。
「TypeScript」
在TypeScript中,當你在編輯器寫代碼時,如有錯誤則會直接拋出異常,極大得提高了效率,也是方便。
五、TypeScript總共圍繞兩種模式展開
顯式注解類型
舉個栗子
let name: string = "前端娛樂圈";
let age: number = 38;
let hobby: string[] = ["write code", "玩游戲"]
顯式注解類型就是,聲明變量時定義上類型(官方話語就是「聲明時帶上注解」),讓我們一看就明白,哦~,這個name是一個string類型。
推導類型
舉個栗子
let name = "前端娛樂圈"; // 是一個string類型
let age = 38; // 是一個number類型
let hobby = ["write code", "玩游戲"] // 是一個string數(shù)組類型
推導類型就是去掉顯示注解,系統(tǒng)自動會識別當前值是一個什么類型的。
六、安裝TypeScript && 運行
typescript
全局安裝typescript環(huán)境。
npm i -g typescript
可是這只是安裝了typescript,那我們怎么運行.ts文件呢,安裝完typescript我們就可以執(zhí)行tsc命令。
如:我們的文件叫做index.ts,直接在命令行執(zhí)行tsc index.ts即可。然后就可以看到在目錄下編譯出來一個index.js,這就是tsc編譯完的結(jié)果。
「index.ts」
const userName: string = "前端娛樂圈"
運行tsc index.ts,你可以看見在index.ts的同級下又生成一個index.js,如下就是編譯的結(jié)果文件index.js。
var userName = "前端娛樂圈"
上面我們知道了運行tsc命令就可以編譯生成一個文件,有的小伙伴覺得這樣太麻煩了,每次運行只是編譯出來一個文件還不是運行,還得用node index.js才可以運行。不急我們接著往下看
ts-node
我們來看一下這個插件ts-node,這個插件可以直接運行.ts文件,并且也不會編譯出來.js文件。
npm i ts-node
// 運行 ts-node index.ts
講到這里我們了解了「為什么要用TypeScript」和它的「優(yōu)缺點」以及它的「運行工作方式」。
那么接下來步入TypeScript基礎知識的海洋啦~,follow me。
感覺有幫助的小伙伴可以關注一下:「前端娛樂圈」 公眾號,謝謝啦~,每天更新一篇小技巧
七、基礎知識
1. 基礎靜態(tài)類型
在TypeScript中基礎類型跟我們JavScript中基礎類型是一樣的。只是有各別是Ts里面新出的。
1. number
const count: number = 18; // 顯示注解一個number類型
const count1 = 18; // 不顯示注解,ts會自動推導出來類型
2. string
const str: string = "前端娛樂圈"; // 顯示注解一個string類型
const str1 = "蛙人"; // 不顯示注解,ts會自動推導出來類型
3. boolean
const status: string = false; // 顯示注解一個string類型
const status1 = true; // 不顯示注解,ts會自動推導出來類型
4. null
const value: null = null;
const value: null = undefined; // 這一點null類型可以賦值undefined跟在 js中是一樣的,null == undefined
5. undefined
const value: undefined = undefined;
const value: undefined = null; // 這一點null類型可以賦值undefined跟在 js中是一樣的,null == undefined
6. void
估計到這有一些小伙伴可能對void這個比較陌生,以為只有TypeScript才有的。其實不是哈,在我們JavaScript就已經(jīng)存在void關鍵字啦,它的意思就是無效的,有的小伙伴可能看見過早些項目里面<a href="javascript: void(0)">這是控制a標簽的跳轉(zhuǎn)默認行為。你不管怎么執(zhí)行void方法它都是返回undefined
那么在我們TypeScript中void類型是什么呢。它也是代表無效的,一般只用在「函數(shù)」上,告訴別人這個「函數(shù)」沒有返回值。
function fn(): void {} // 正確
function testFn(): void {
return 1; // 報錯,不接受返回值存在
}
function fn1(): void { return undefined} // 顯示返回undefined類型,也是可以的
function fn2(): void { return null} // 顯示返回null類型也可以,因為 null == undefined
7. never
never「一個永遠不會有值的類型」或者也可以說「一個永遠也執(zhí)行不完的類型」,代表用于不會有值,undefined、null也算做是值。一般這個類型就不會用到,也不用。大家知道這個類型就行。
const test: never = null; // 錯誤
const test1: never = undefined // 錯誤
function Person(): never { // 正確,因為死循環(huán)了,一直執(zhí)行不完
while(true) {}
}
function Person(): never { // 正確,因為遞歸,永遠沒有出口
Person()
}
function Person(): never { // 正確 代碼報錯了,執(zhí)行不下去
throw new Error()
}
8. any
any這個類型代表「任何的」、「任意的」。希望大家在項目中,不要大片定義any類型。雖然它真的好使,那這樣我們寫TypeScript就沒有任何意義了。
let value: any = ""; // 正確
value = null // 正確
value = {} // 正確
value = undefined // 正確
9. unknown
unknown類型是我們TypeScript中第二個any類型,也是接受任意的類型的值。它的英文翻譯過來就是「未知的」,我們來看一下栗子
let value: unknown = ""
value = 1;
value = "fdsfs"
value = null
value = {}
那現(xiàn)在肯定有小伙伴疑惑,誒,那它unknown相當于是any類型,那二者的區(qū)別是什么。我們來看一下
let valueAny: any = "";
let valueUnknown: unknown = "";
valueAny = "蛙人";
valueUnknown = "前端娛樂圈"
let status: null = false;
status = valueAny; // 正確
status = valueUnknown // 報錯,不能將unknown類型分配給null類型
我們來看一下上面的,為什么any類型就能被賦值成功,而unknown類型不行呢,從它倆的意義來上看,還是有點區(qū)別的,any任何的,任意的、unknown未知的。所以你給unknown類型賦值任何類型都沒關系,因為它本來就是未知類型嘛。但是你如果把它的unknown類型去被賦值一個null類型,這時人家null這邊不干了,我不接受unknown類型。
說白了一句話,別人不接受unknown類型,而unknown類型接受別人,哈哈哈哈。
2. 對象靜態(tài)類型
說起對象類型,我們肯定都能想到對象包含{}、數(shù)組、類、函數(shù)
1. object && {}
其實這倆意思一樣,{}、object表示非原始類型,也就是除number,string,boolean,symbol,null或undefined之外的類型。
const list: object = {} // 空對象
const list1: object = null; // null對象
const list: object = [] // 數(shù)組對象
const list: {} = {}
list.name = 1 // 報錯 不可更改里面的字段,但是可以讀取
list.toString()
2. 數(shù)組
const list: [] = []; // 定義一個數(shù)組類型
const list1: number[] = [1,2] // 定義一個數(shù)組,里面值必須是number
const list2: object[] = [null, {}, []] // 定義一個數(shù)組里面必須是對象類型的
const list3: Array<number> = [1,2,3] // 泛型定義數(shù)組必須是number類型,泛型我們待會講到
3. 類
// 類
class ClassPerson = {
name: "前端娛樂圈"
}
const person: ClassPerson = new Person();
person.xxx = 123; // 這行代碼報錯,因為當前類中不存在該xxx屬性
4. 函數(shù)
// 函數(shù)
const fn: () => string = () => "前端娛樂圈" // 定義一個變量必須是函數(shù)類型的,返回值必須是string類型
3. 函數(shù)類型注解
這里說一下函數(shù)顯示注解和函數(shù)參數(shù)不會類型推導問題。
1. 函數(shù)返回類型為number
function fn(a, b): number {
return a + b;
}
fn(1, 2)
2. 函數(shù)void
顯示注解為void類型,函數(shù)沒有返回值。
function fn(): void {
console.log(1)
}
3. 函數(shù)不會自動類型推導
可以看到下面的函數(shù)類型,不會自動類型推導,我們實參雖然傳入的1和2,但是形參方面是可以接受任意類型值的,所以系統(tǒng)也識別不出來你傳遞的什么,所以這里得需要我們顯示定義注解類型。
function testFnQ(a, b) {
return a + b
}
testFnQ(1,2)

我們來改造一下。
function testFnQ(a:number, b:number) {
return a + b
}
testFnQ(1,2)

我們再來看一下參數(shù)對象顯示注解類型,也是在:號后面賦值每個字段類型即可。
function testFnQ(obj : {num: number}) {
return obj.num
}
testFnQ({num: 18})
4. 元組Tuple
元組用于表示一個已知數(shù)組的數(shù)量和類型的數(shù)組,定義數(shù)組中每一個值的類型,一般不經(jīng)常使用。
const arr: [string, number] = ["前端娛樂圈", 1]
const arr: [string, string] = ["前端娛樂圈", 1] // 報錯
5. 枚舉Enum
Enum枚舉類型,可以設置默認值,如果不設置則為索引。
enum color {
RED,
BLUE = "blue",
GREEN = "green"
}
// color["RED"] 0
// color["BLUE"] blue
像上面的color中RED沒有設置值,那么它的值則為0,如果BLUE也不設置的話那么它的值則是1,它們這里是遞增。如果設置值則是返回設置的值
「注意這里還有一個問題,直接來上代碼」
通過上面學習我們知道了enum可以遞增值,也可以設置默認值。但是有一點得注意一下,enum沒有json對象那樣靈活,enum不能在任意字段上設置默認值。
比如下面栗子,RED沒有設置值,然后BLUE設置了默認值,但是GREEN又沒有設置,這時這個GREEN會報錯。因為你第二個BLUE設置完默認值,第三又不設置,這時代碼都不知道該咋遞增了,所以報錯。還有一種方案就是你給BLUE可以設置一個數(shù)字值,這時第三個GREEN不設置也會跟著遞增,因為都是number類型。
// 報錯
enum color {
RED,
BLUE = "blue",
GREEN
}
// good
enum color {
RED, // 0
BLUE = 4, // 4
GREEN // 5
}
比如enum枚舉類型還可以反差,通過value查key值。像我們json對象就是不支持這種寫法的。
enum color {
RED, // 0
BLUE = 4, // 4
GREEN // 5
}
console.log(color[4]) // BLUE
console.log(color[0]) // RED
5. 接口Interface
接口interface是什么,接口interface就是方便我們定義一處代碼,多處復用。接口里面也存在一些修飾符。下面我們來認識一下它們吧。
1. 接口怎么復用
比如在講到這之前,我們不知道接口這東西,可能需要給對象定義一個類型的話,你可能會這樣做。
const testObj: { name: string, age: number } = { name: "前端娛樂圈", age: 18 }
const testObj1: { name: string, age: number } = { name: "蛙人", age: 18 }
我們用接口來改造一下。
interface Types {
name: string,
age: number
}
const testObj: Types = { name: "前端娛樂圈", age: 18 }
const testObj1: Types = { name: "蛙人", age: 18 }
可以看到使用interface關鍵字定義一個接口,然后賦值給這兩個變量,實現(xiàn)復用。
2. readonly修飾符
readonly類型,只可讀狀態(tài),不可更改。
interface Types {
readonly name: string,
readonly age: number
}
const testObj: Types = { name: "前端娛樂圈", age: 18 }
const testObj1: Types = { name: "蛙人", age: 18 }
testObj.name = "張三" // 無法更改name屬性,因為它是只讀屬性
testObj1.name = "李四" // 無法更改name屬性,因為它是只讀屬性
3. ?可選修飾符
可選修飾符以?定義,為什么需要可選修飾符呢,因為如果我們不寫可選修飾符,那interface里面的屬性都是必填的。
interface Types {
readonly name: string,
readonly age: number,
sex?: string
}
const testObj: Types = { name: "前端娛樂圈", age: 18}
4. extends繼承
我們的interface也是可以繼承的,跟「ES6」Class類一樣,使用extends關鍵字。
interface Types {
readonly name: string,
readonly age: number,
sex?: string
}
interface ChildrenType extends Types { // 這ChildrenType接口就已經(jīng)繼承了父級Types接口
hobby: []
}
const testObj: ChildrenType = { name: "前端娛樂圈", age: 18, hobby: ["code", "羽毛球"] }
5. propName擴展
interface里面這個功能就很強大,它可以寫入不在interface里面的屬性。
interface Types {
readonly name: string,
readonly age: number,
sex?: string,
}
const testObj: Types = { name: "前端娛樂圈", age: 19, hobby: [] }
上面這個testObj這行代碼會爆紅,因為hobby屬性不存在interface接口中,那么我們不存在的接口中的,還不讓人家寫了?。這時候可以使用「自定義」就是上面的propName。
interface Types {
readonly name: string,
readonly age: number,
sex?: string,
[propName: string]: any // propName字段必須是 string類型 or number類型。 值是any類型,也就是任意的
}
const testObj: Types = { name: "前端娛樂圈", age: 19, hobby: [] }
在運行上面代碼,就可以看到不爆紅了~
6. Type
我們再來看一下Type,這個是聲明類型別名使的,別名類型只能定義是:基礎靜態(tài)類型、對象靜態(tài)類型、元組、聯(lián)合類型。
?注意:type別名不可以定義interface
?
type Types = string;
type TypeUnite = string | number
const name: typeUnite = "前端娛樂圈"
const age: typeUnite = 18
1. 那么type類型別名和interface接口有什么區(qū)別呢
1. type不支持interface聲明
type Types = number
type Types = string // 報錯, 類型別名type不允許出現(xiàn)重復名字
interface Types1 {
name: string
}
interface Types1 {
age: number
}
// interface接口可以出現(xiàn)重復類型名稱,如果重復出現(xiàn)則是,合并起來也就是變成 { name:string, age: number }
第一個Types類型別名type不允許出現(xiàn)重復名字,interface接口可以出現(xiàn)重復類型名稱,如果重復出現(xiàn)則是,合并起來也就是變 { name:string, age: number }
「再來看一下interface另一種情況」
interface Types1 {
name: string
}
interface Types1 {
name: number
}
可以看到上面兩個同名稱的interface接口,里面的屬性也是同名稱,但是類型不一樣。這第二個的Types1就會爆紅,提示:「后續(xù)聲明的接口,必須跟前面聲明的同名屬性類型必須保持一致」,把后續(xù)聲明的name它類型換成string即可。
2. type支持表達式 interface不支持
const count: number = 123
type testType = typeof count
const count: number = 123
interface testType {
[name: typeof count]: any // 報錯
}
可以看到上面type支持表達式,而interface不支持
3. type 支持類型映射,interface不支持
type keys = "name" | "age"
type KeysObj = {
[propName in keys]: string
}
const PersonObj: KeysObj = { // 正常運行
name: "蛙人",
age: "18"
}
interface testType {
[propName in keys]: string // 報錯
}
7. 聯(lián)合類型
聯(lián)合類型用|表示,說白了就是滿足其中的一個類型就可以。
const statusTest: string | number = "前端娛樂圈"
const flag: boolean | number = true
再來看一下栗子。我們用函數(shù)參數(shù)使用「聯(lián)合類型」看看會發(fā)生什么
function testStatusFn(params: number | string) {
console.log(params.toFixed()) // 報錯
}
testStatusFn(1)
上面我們說過了,函數(shù)參數(shù)類型不能類型自動推導,更何況現(xiàn)在用上「聯(lián)合類型」,系統(tǒng)更懵逼了,不能識別當前實參的類型。所以訪問當前類型上的方法報錯。
接下來帶大家看一些類型保護,聽著挺高級,其實這些大家都見過。別忘了記得關注:「前端娛樂圈」 公眾號哦,嘻嘻
1. typeof
function testStatusFn(params: number | string) {
console.log(params.toFixed()) // 報錯
}
testStatusFn(1)
「改造后」
// 正常
function testStatusFn(params: string | number) {
if (typeof params == "string") {
console.log(params.split)
}
if (typeof params == "number") {
console.log(params.toFixed)
}
}
testStatusFn(1)
2. in
// 報錯
interface frontEnd {
name: string
}
interface backEnd {
age: string
}
function testStatusFn(params: frontEnd | backEnd) {
console.log(params.name)
}
testStatusFn({name: "蛙人"})
「改造后」
// 正常
function testStatusFn(params: frontEnd | backEnd) {
if ("name" in params) {
console.log(params.name)
}
if ("age" in params) {
console.log(params.age)
}
}
testStatusFn({name: "蛙人"})
3. as 斷言
// 報錯
interface frontEnd {
name: string
}
interface backEnd {
age: string
}
function testStatusFn(params: frontEnd | backEnd) {
console.log(params.name)
}
testStatusFn({name: "蛙人"})
「改造后」
// 正常
function testStatusFn(params: frontEnd | backEnd) {
if ("name" in params) {
const res = (params as frontEnd).name
console.log(res)
}
if ("age" in params) {
const res = (params as backEnd).age
console.log(res)
}
}
testStatusFn({age: 118})
8. 交叉類型
交叉類型就是跟聯(lián)合類型相反,它用&表示,交叉類型就是兩個類型必須存在。這里還用上面的「聯(lián)合類型」的栗子來看下。
interface frontEnd {
name: string
}
interface backEnd {
age: number
}
function testStatusFn(params: frontEnd & backEnd) {}
testStatusFn({age: 118, name: "前端娛樂圈"})
這里我們可以看到實參必須傳入兩個**接口(interface)**全部的屬性值才可以。「聯(lián)合類型」是傳入其中類型就可以。
「注意:我們的接口interface出現(xiàn)同名屬性」
interface frontEnd {
name: string
}
interface backEnd {
name: number
}
function testStatusFn(params: frontEnd & backEnd) {
console.log(params)
}
testStatusFn({name: "前端"})
上面我們兩個接口類型中都出現(xiàn)了同名屬性,但是類型不一樣,這時類型就會變?yōu)?code style="overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;">never。

9. 泛型
泛型是TypeScript中最難理解的了,這里我盡量用通俗易懂的方式講明白。
function test(a: string | number, b: string | number) {
console.log(a, b)
}
test(1, "前端娛樂圈")
比如上面栗子,函數(shù)參數(shù)注解類型定義string和number,調(diào)用函數(shù)實參傳入也沒什么問題,但是有個需求,就是實參我們「必須傳入同樣的類型」(傳入兩個number類型)。雖然上面這種「聯(lián)合類型」也可以實現(xiàn),但是如果我們要在加一個boolean類型,那么「聯(lián)合類型」還得在追加一個boolean,那這樣代碼太冗余了。
這時就需要用到「泛型」了,「泛型」是專門針對不確定的類型使用,并且靈活。泛型的使用大部分都是使用<T>,當然也可以隨便使用,如:<Test>、<Custom>都可以。
function test<T>(a: T, b: T) {
console.log(a, b)
}
test<number>(1, "前端娛樂圈") // 調(diào)用后面跟著尖括號這就是泛型的類型,這時報錯,因為在調(diào)用的使用類型是number,所以只能傳入相同類型的
test<boolean>(true, false)
test<string>("前端娛樂圈", "蛙人")
上面這使用「泛型」就解決了我們剛才說的傳入同一個類型參數(shù)問題,但是「泛型」也可以使用不同的參數(shù),可以把調(diào)用類型定義為<any>
function test<T>(a: T, b: T) {
console.log(a, b)
}
test<any>(1, "前端娛樂圈")
但是上面這種又有一種問題,它可以傳入對象,但是如果我們只希望傳入number類型和string類型。那么我們「泛型」也給我們提供了**約束「類型。「泛型」使用extends進行了」類型約束**,只能選擇string、number類型。
function test<T extends number | string, Y extends number | string>(a: T, b: Y) {
console.log(a, b)
}
test<number, string>(18, "前端娛樂圈")
test<string, number>("前端娛樂圈", 18)
這時,傳入泛型時使用,逗號分隔,來定義每一個類型希望是什么。記住,只有我們不確定的類型,可以使用泛型。
10. 模塊
TypeScript也支持import和export這里大多數(shù)小伙伴都知道,這里都不多講啦。
// 導入
import xxx, { xxx } from "./xxx"
// 導出
export default {}
export const name = "前端娛樂圈"
如有不明白的小伙伴,可以看我以前文章 聊聊什么是CommonJs和Es Module及它們的區(qū)別
11. Class類
?以下這三個修飾符是在
?TypeScript類中才能使用,在JavaScript類中是不支持的。

1. public
public為類的公共屬性,就是不管在類的內(nèi)部還是外部,都可以訪問該類中「屬性」及「方法」。默認定義的「屬性」及「方法」都是public。
class Person {
name = "前端娛樂圈";
public age = 18;
}
const res = new Person();
console.log(res.name, res.age) // 前端娛樂圈 18
上面可以看到打印結(jié)果都能顯示出來,name屬性沒有定義public公共屬性,所以類里面定義的「屬性」及「方法」默認都是public定義。
2. private
private為類的私有屬性,只有在當前類里面才能訪問,當前類就是{}里面區(qū)域內(nèi)。在{}外面是不能訪問private定義的「屬性」及「方法」的
class Person {
private name = "前端娛樂圈";
private age = 18;
}
const res = new Person();
console.log(res.name, res.age) // 這倆行會爆紅,當前屬性為私有屬性,只能在類內(nèi)部訪問
class Scholl extends Person {
getData() {
return this.username + "," + this.age
}
}
const temp = new Scholl()
console.log(temp.getData()) // 爆紅~,雖然繼承了Person類,但是private定義是只能在當前類訪問,子類也不能訪問。
3. protected
protected為類的保護屬性,只有在「當前類」和「子類」可以訪問。也就是說用protected屬性定義的「子類」也可以訪問。
class Person {
protected username = "前端娛樂圈";
protected age = 18;
}
const res = new Person();
console.log(res.name, res.age) // 這倆行會爆紅,當前屬性為私有屬性,只能在類內(nèi)部訪問
class Scholl extends Person {
getData() {
return this.username + "," + this.age
}
}
const temp = new Scholl()
console.log(temp.getData()) // 前端娛樂圈,18??梢哉TL問父類的屬性
4. implements
implements關鍵字只能在class中使用,顧名思義,實現(xiàn)一個新的類,從父級或者從接口實現(xiàn)所有的屬性和方法,如果在PersonAll類里面不寫進去接口里面已有的屬性和方法則會報錯。
interface frontEnd {
name: string,
fn: () => void
}
class PersonAll implements frontEnd {
name: "前端娛樂圈";
fn() {
}
}
5. 抽象類
抽象類使用abstract關鍵字定義。abstract抽象方法不能實例化,如果,抽象類里面方法是抽象的,那么本身的類也必須是抽象的,抽象方法不能寫函數(shù)體。父類里面有抽象方法,那么子類也必須要重新該方法。
// 抽象類
abstract class Boss {
name = "秦";
call() {} // 抽象方法不能寫函數(shù)體
}
class A extends Boss {
call() {
console.log(this.name);
console.log("A")
}
}
class B extends Boss {
call() {
console.log("B")
}
}
new A().call()
該抽象類使用場景,比如A需求或者B需求正好需要一個公共屬性,然后本身還有一些自己的邏輯,就可以使用抽象類,抽象類只能在TypeScript中使用。
12. 命名空間namespace
我們學到現(xiàn)在可以看到,不知道小伙伴們發(fā)現(xiàn)沒有,項目中文件是不是不能有重復的變量(不管你是不是一樣的文件還是其它文件),否則就直接爆紅了。命名空間一個最明確的目的就是解決重名問題。
命名空間使用namespace關鍵字來定義,來看栗子吧。
「index.ts」
namespace SomeNameSpaceName {
const q = {}
export interface obj {
name: string
}
}
上面這樣,就定義好了一個命名空間,可以看到變量q沒有寫export關鍵字,這證明它是內(nèi)部的變量,就算別的.ts文件引用它,它也不會暴露出去。而interface這個obj接口是可以被全局訪問的。
「我們在別的頁面訪問當前命名空間」
1. reference引入
/// <reference path="./index.ts" />
namespace SomeNameSpaceName {
export class person implements obj {
name: "前端娛樂圈"
}
}
2. import
export interface valueData {
name: string
}
import { valueData } from "./xxx.ts"
這時使用命名空間之后完全可以解決不同文件重名爆紅問題。
13. tsConfig.json
這個tsconfig文件,是我們編譯ts文件,如何將ts文件編譯成我們的js文件。tsc -init這個命令會生成該文件出來哈。執(zhí)行完該命令,我們可以看到根目錄下會生成一個tsconfig.json文件,里面有一堆屬性。
那么我們怎么將ts文件編譯成js文件呢,直接執(zhí)行tsc命令可以將根目錄下所有的.ts文件全部編譯成.js文件輸出到項目下。
更多配置文檔,請參考官網(wǎng)
{
// include: ["*.ts"] // 執(zhí)行目錄下所有的ts文件轉(zhuǎn)換成js文件
// include: ["index.ts"] // 只將項目下index.ts文件轉(zhuǎn)換為js文件
// files: ["index.ts"] // 跟include一樣,只執(zhí)行當前數(shù)組值里面的文件,當前files必須寫相對路徑
// exclude: ["index.ts"] // exclude就是除了index.ts不執(zhí)行,其它都執(zhí)行
compilerOptions: {
removeComments: true, // 去掉編譯完js文件的注釋
outDir: "./build", // 最終輸出的js文件目錄
rootDir: "./src", // ts入口文件查找
}
}
八、實用類型
最后來說一下實用類型,TypeScript標準庫自帶了一些實用類型。這些實用類都是方便接口Interface使用。這里只列舉幾個常用的,更多實用類型官網(wǎng)
1. Exclude
從一個類型中排除另一個類型,只能是「聯(lián)合類型」,從TypesTest類型中排除UtilityLast類型。
「適用于:并集類型」
interface UtilityFirst {
name: string
}
interface UtilityLast {
age: number
}
type TypesTest = UtilityFirst | UtilityLast;
const ObjJson: Exclude<TypesTest, UtilityLast> = {
name: "前端娛樂圈"
}
2. Extract
Extract正好跟上面那個相反,這是選擇某一個可賦值的「聯(lián)合類型」,從TypesTest類型中只選擇UtilityLast類型。
「適用于:并集類型」
interface UtilityFirst {
name: string
}
interface UtilityLast {
age: number
}
type TypesTest = UtilityFirst | UtilityLast;
const ObjJson: Extract<TypesTest, UtilityLast> = {
age: 1
}
3. Readonly
把數(shù)組或?qū)ο蟮乃袑傩灾缔D(zhuǎn)換為只讀的。這里只演示一下對象栗子,數(shù)組同樣的寫法。
「適用于:對象、數(shù)組」
interface UtilityFirst {
name: string
}
const ObjJson: Readonly<UtilityFirst> = {
name: "前端娛樂圈"
}
ObjJson.name = "蛙人" // 報錯 只讀狀態(tài)
4. Partial
把對象的所有屬性設置為選的。我們知道interface只要不設置?修飾符,那么對象都是必選的。這個實用類可以將屬性全部轉(zhuǎn)換為可選的。
「適用于:對象」
interface UtilityFirst {
name: string
}
const ObjJson: Partial<UtilityFirst> = {
}
5. Pick
Pick選擇對象類型中的部分key值,提取出來。第一個參數(shù)目標值,第二個參數(shù)「聯(lián)合」key
「適用于:對象」
interface UtilityFirst {
name: string,
age: number,
hobby: []
}
const ObjJson: Pick<UtilityFirst, "name" | "age"> = {
name: "前端娛樂圈",
age: 18
}
6. Omit
Omit選擇對象類型中的部分key值,過濾掉。第一個參數(shù)目標值,第二個參數(shù)「聯(lián)合」key
「適用于:對象」
interface UtilityFirst {
name: string,
age: number,
hobby: string[]
}
const ObjJson: Omit<UtilityFirst, "name" | "age"> = {
hobby: ["code", "羽毛球"]
}
7. Required
Required把對象所有可選屬性轉(zhuǎn)換成必選屬性。
「適用于:對象」
interface UtilityFirst {
name?: string,
age?: number,
hobby?: string[]
}
const ObjJson: Required<UtilityFirst> = {
name: "蛙人",
age: 18,
hobby: ["code"]
}
8. Record
創(chuàng)建一個對象結(jié)果集,第一個參數(shù)則是key值,第二個參數(shù)則是value值。規(guī)定我們只能創(chuàng)建這里面字段值。
「適用于:對象」
type IndexList = 0 | 1 | 2
const ObjJson: Record<IndexList, "前端娛樂圈"> = {
0: "前端娛樂圈",
1: "前端娛樂圈",
2: "前端娛樂圈"
參考資料
聊聊什么是CommonJs和Es Module及它們的區(qū)別: https://juejin.cn/post/6938581764432461854
[2]官網(wǎng): https://www.tslang.cn/docs/handbook/compiler-options.html
[3]官網(wǎng): https://www.typescriptlang.org/docs/handbook/utility-types.html
[4]了不起的 TypeScript 入門教程: https://juejin.cn/post/6844904182843965453
[5]TypeScript中文網(wǎng): https://www.tslang.cn/docs/handbook/basic-types.html
請你喝杯?? 記得三連哦~
1.閱讀完記得給?? 醬點個贊哦,有?? 有動力
2.關注公眾號前端那些趣事,陪你聊聊前端的趣事
3.文章收錄在Github frontendThings 感謝Star?
