今天繼續來分享ts的相關概念,枚舉,ts模塊化,接口和類型兼容性
ts的擴展類型:類型別名,枚舉,接口和類
枚舉
基礎概念
枚舉通常用于約束某個變量的取值范圍。當然字面量和聯合類型配合使用,也可以達到同樣的目標。
為什么使用枚舉
但是使用字面量和聯合聯系會存在一個問題
- 邏輯含義和真實值容易混淆,修改真實值的時候,會產生大量的修改
- 字面量類型不會進入編譯結果
使用枚舉的話就不會出現這種問題
如何定義枚舉
/*
enum 枚舉名{枚舉字段1 = 值1,枚舉字段2 = 值2,..
}
*///實例enum Gender {Male = "帥哥",Female = "美女"}// 先生 女士 男 女 male femalelet gender: Gender;gender = Gender.Male;gender = Gender.Female;
編譯后的js代碼
var Gender;
(function (Gender) {Gender["Male"] = "\u5E05\u54E5";Gender["Female"] = "\u7F8E\u5973";
})(Gender || (Gender = {}));
// 先生 女士 男 女 male female
let gender;
gender = Gender.Male;
gender = Gender.Female;
下面給個使用聯合類型的例子
type Gender = "男" | "女";
let g:Gender;g = '女';
g = '男';
編譯后的js代碼
let g;
g = '女';
g = '男';
可以非常清晰的看出枚舉的好處,會把枚舉里的屬性也編譯到結果里面,避免了代碼里面大量的真實值。
枚舉的規則
- 枚舉的字段值可以是字符串或數字
- 數字枚舉的值會自動自增
enum Level {level1 : 1,level2,level3
}let l: Level = Level.level1;
console.log(l)
l = Level.level2;
console.log(l)
l = Level.level3;
console.log(l)
控制臺輸出結果
1
2
3
-
被數字枚舉約束的變量,可以直接賦值為數字
-
數字枚舉的編譯結果 和 字符串枚舉有差異
小練習
這里使用枚舉來做測試
//權限
enum Permission {Read = 1, //0001Write = 2, //0010Create = 4, //0100Delete = 8 //1000
}//1.使用或運算組合權限
let p: Permission = Permission.Read | Permission.Write;//這樣p就具備read和write權限//2.判斷是否擁有某個權限,使用&,且運算兩邊都是1才為1
function hasPermisson(target: Permission, per: Permission) {return (target & per) === per;
}//3.如何刪除某個權限
//通過異或運算,相同取0,不同取1
p = p ^ Permission.Write;
模塊化
配置名稱 | 含義 |
---|---|
module | 設置編譯結果中使用的模塊化標準 |
moduleResolution | 設置解析模塊的模式 |
noImplicitUseStrict | 編譯結果中不包含"use strict" |
removeComments | 編譯結果移除注釋 |
noEmitOnError | 錯誤時不生成編譯結果 |
esModuleInterop | 啟動es模塊化交互非es模塊導出 |
前端模塊化標準:ES6、commonjs、amd、umd、system,esnext
TS中如何書寫模塊化語句
TS中,導入和導出模塊,統一使用ES6的模塊化標準
編譯結果中的模塊化
可配置
TS中的模塊化在編譯結果中:
- 如果編譯結果的模塊化標準是ES6:沒有區別
- 如果編譯結果的模塊化標準是commonjs:導出的聲明會變成exports的屬性,默認的導出會變成exports的default屬性;
默認是用的es6語法
import fs from "fs";
import myModule from "./myModule";
如何在TS中書寫commonjs模塊化代碼
如果想用commonjs語法的話需要在tsconfig.json里面配置
{"compilerOptions": { //編譯選項"module": "CommonJS", //配置編譯目標使用的模塊化標準},
}
代碼:
//導出
export = xxx//導入
import xxx = require(xxx)
示例
//modules.ts
export = {name: 'kakarote',sum(a: number, b: number) {return a + b;}
}//index.ts
import myModule = require('./myModule');
模塊解析
關于ts的一個模塊解析策略,它有兩種模塊解析策略
具體可以查看中文網:https://www.tslang.cn/docs/handbook/module-resolution.html,也可以去官網里查看
-
classic:經典模塊解析策
//一、對于相對路徑引入的模塊流程 //root/src/folder/A.ts import { b } from "./moduleB"//這樣引入的話首先它會查找 //1. /root/src/folder/moduleB.ts //2. /root/src/folder/moduleB.d.ts//二、對于非相對路徑的導入 //編譯器則會從包含導入文件的目錄開始依次向上級目錄遍歷,嘗試定位匹配的聲明文件。 //root/src/folder/A.ts import { b } from "moduleB" //它會這么查找 //1./root/src/folder/moduleB.ts //2./root/src/folder/moduleB.d.ts //3./root/src/moduleB.ts //4./root/src/moduleB.d.ts //5./root/moduleB.ts //6./root/moduleB.d.ts //7./moduleB.ts //8./moduleB.d.ts
-
node:模塊解析策略
//一、對于相對路徑引入的解析模塊流程 ///root/src/moduleA.js var x = require("./moduleB");//1.它會監測:/root/src/moduleB.js文件是否存在 //2.監測/root/src/moduleB目錄是否包含package.json模塊,如果package.json指定了main模塊,包含了{ "main": "lib/mainModule.js" },那么它就會引用/root/src/moduleB/lib/mainModule.js //3.監測/root/src/moduleB目錄是否包含index.js,如果存在它就會被當做那個文件夾的main模塊//二、非相對模塊的解析流程 ///root/src/moduleA.js var x = require("moduleB")//1./root/src/node_modules/moduleB.js //2./root/src/node_modules/moduleB/package.json (如果指定了"main"屬性) //3./root/src/node_modules/moduleB/index.js//4./root/node_modules/moduleB.js //5./root/node_modules/moduleB/package.json (如果指定了"main"屬性) //6.root/node_modules/moduleB/index.js//7./node_modules/moduleB.js //8./node_modules/moduleB/package.json (如果指定了"main"屬性) //9./node_modules/moduleB/index.js注意Node.js在步驟(4)和(7)會向上跳一級目錄。
接口
TypeScript的接口:用于約束類、對象、函數的契約(標準)
契約(標準)的形式:
-
API文檔,弱標準
-
代碼約束,強標準
和類型別名一樣,接口,不出現在編譯結果中
接口約束對象
interface User {name: stringage: numbersayHello: () => void
}//定義類型別名
// type User = {
// name: string,
// age: number,
// sayHello: () => void
// }let u: User = {name: "sdsad",age: 33,sayHello() {console.log('asdasdsasa')}
}
接口可以繼承
class Banner extends React.Component{}
可以通過接口之間的繼承,實現多種接口的組合
使用類型別名可以實現類型的組合效果,需要通過&
,它叫做交叉類型
它們的區別:
- 子接口不能覆蓋父接口的成員
- 交叉類型會把相同成員的類型進行交叉
readonly
只讀修飾符,修飾的目標是只讀
只讀修飾符不在編譯結果中
類型兼容性
B->A,如果能完成賦值,則B和A類型兼容
鴨子辨型法(子結構辨型法):目標類型需要某一些特征,賦值類型只要能滿足該特征即可
例子:
interface Duck {sound: "嘎嘎嘎",swim(): void
}let person:Duck = {name: "偽裝成鴨子的人",age: 1,sound: "嘎嘎嘎" as "嘎嘎嘎",swim() {console.log(this.name + '正在游泳,并發出了' + this.sound + "的聲音")}
}
如果直接給person設置Duck類型約束會報錯
但是可以使用下面這種方式
interface Duck {sound: "嘎嘎嘎",swim(): void
}let person = {name: "偽裝成鴨子的人",age: 1,sound: "嘎嘎嘎" as "嘎嘎嘎",swim() {console.log(this.name + '正在游泳,并發出了' + this.sound + "的聲音")}
}let duck: Duck = person;
如果直接用對象的字面量形式就會報錯,但是賦值的形式它就會忽略,只要有sound和swim就不會報錯
函數類型
我們可以看到arr的forEach的一個函數的ts定義
forEach(callbackfn: (value: number, index: number, array: number[]) => void, thisArg?: any): void
它這里要求這個回調函數的參數有這些,value,index,array,但是這里我們傳遞的時候可以少傳遞,但是不能多傳遞。
[34, 3].forEach(it => console.log(it));
Tips:
參數值:傳遞給目標函數參數可以少,不能多
返回值:如果函數要求返回一定要返回,不要求返回則可以隨意
結語
今天的學習回顧就到這里了!!