前言
vue3已經發布了,ts的腳步已經阻攔不住了,還只會es6?別想了,人家都已經在行動了,以下是ts的基本系列教程,ts的基本語法,高階語法等,以及在vue專案中如何應用ts,跟著我趕緊擼起來吧。
基本資料型別
數字
const a: number = 3;
字串
const b: string = "1";
陣列
const c: number[] = [1, 2, 3];
const d: Array<number> = [1, 3];
const arr: any[] = [1, "33", true];
元組
可以為陣列中的每個引數定義相對應的型別
const e: [number, string] = [1, "ww"];
列舉
enum error {
blue = 3,
"orange",
}
const f: error = error.orange;
console.log(f); //輸出4
tips
- 如果
未賦值
的上一個值是數字
那麼這個未賦值的值
的是上一個值的值+1- 如果
未賦值
的上一個值未賦值
那麼輸出的就是它的下標
- 如果
未賦值的上一個值的值是非數字
,那麼必須賦值
布林型別
const g: boolean = true;
物件
const i: object = {};
undefined
常用於組合型別
let j: number | undefined;
null
let k: null;
void
指定方法型別,表示沒有返回值,方法體中不能return
function aa(): void {
console.log(1);
}
//如果方法有返回值,可以加上返回值的型別
function bb(): number {
return 1;
}
never
其他型別 (包括null和undefined)的子型別,代表從不會出現的值
let l: never;
//匿名函式並丟擲異常
l = (() => {
throw new Error("111");
})();
任意型別
讓引數可以是任何一種型別
let h: any = 1;
h = true;
h = "st";
函式
函式申明
function cc(): void {}
方法傳參
function getUserInfo(name: string, age?: number, school: string = "清華大學") {
return `name:${name}--age:${age}--school:${school}`;
}
tips: ?代表這個引數可傳可不傳,不傳就是undefined,也可定義個預設的值
剩餘引數
傳遞多個時,如果用了剩餘引數,就可以把未定義的形參轉換為陣列。
function sum (a: number, b: number, ...arr: number[]): number {
let sum: number = a + b;
arr.forEach((element) => {
sum += element;
});
console.log(arr); [3,4,5]
return sum;
}
console.log(sum(1, 2, 3, 4, 5)); //15
函式過載
function reload(name: string): string;
function reload(age: number): string;
function reload(param: any): any {
return typeof param === "string" ? `我是:${param}` : `我的年齡:${param}`;
}
console.log(reload(18)); //年齡
tips:被過載
的方法,是沒有方法體
,可以根據引數的型別走其中一個方法並判斷引數,但如果傳入的引數型別不是任何被過載方法的引數型別
就不允許通過。
第 1 個過載(共 2 個),“(name: string): string”,出現以下錯誤。
型別“never[]”的引數不能賦給型別“string”的引數。
第 2 個過載(共 2 個),“(age: number): string”,出現以下錯誤。
型別“never[]”的引數不能賦給型別“number”的引數
類
class Person {
// 私有變數
private name: string;
// 建構函式
constructor(name: string) {
this.name = name;
}
// 獲取名字
getName(): string {
return this.name;
}
// 設定名字
setName(name: string): void {
this.name = name;
}
}
let p = new Person("張三");
p.setName("李四");
console.log(p);
繼承
class Son extends Person {
// 靜態屬性
public static age: number = 18;
// 學校
public school: string;
//構造方法
constructor(name: string, school: string) {
// 訪問派生類的建構函式中的 "this" 前,必須呼叫 "super",初始化父類建構函式 --並把引數傳給父類
super(name);
//把傳進來的school賦值給全域性變數
this.school = school;
}
//靜態方法
static run(name: string): string {
return `${name}在跑步,他的年齡才${this.age}`;
}
}
let son = new Son("王五", "清華大學");
son.setName("趙六"); // 私有類也不能在子類的外部訪問,但可通過公開的方法中進行賦值和訪問
console.log(son);
console.log(Son.run("方七"));
console.log(Son.age);
tips:
- public 在當前類裡面,子類,類外面都可以訪問
- protected 在當前類和子類內部可以訪問,類外部無法訪問
- private 在當前類內部可訪問,子類,類外部都無法訪問。
- 屬性不加修飾符,預設就是公有的 (public)
多型
通過抽象方法/方法過載--實現多型--多型的作用是用來定義標準
// 抽象父類
abstract class Animal {
private name: string;
constructor(name: string) {
this.name = name;
}
//抽象成員--方法
abstract eat(): any;
//抽象成員--屬性
protected abstract ages: Number;
sleep(): void {
console.log("睡覺");
}
}
class cat extends Animal {
ages: Number = 2;
constructor(name: string) {
super(name);
}
//非抽象類“cat”不會自動實現繼承自“Animal”類的抽象成員“eat”, 必須手動定義父類中的抽象方法--多型
eat(): string {
return "貓吃魚";
}
//多型
sleep(): string {
return "貓在睡覺";
}
}
console.log(new cat("33").sleep());
tips:
- 抽象類無法
例項化
。- 非抽象類繼承抽象父類時
不會自動實現
來自父類的抽象成員,必須手動定義
父類中的抽象成員,否則報錯。- 抽象成員包括
屬性
和方法
介面
在物件導向的程式設計中,介面是一種規範的定義,它定義了行為和動作的規範,
在程式設計裡面,介面起到一種限制和規範的作用。
介面定義了某一批類所需要遵守的規範,介面不關心這些類的內部狀態資料,也不關心這些類裡方法的實現細節,它只規定這批類裡必須提供某些方法,提供這些方法的類就可以滿足實際需要。ts中的介面類似於java,同時還增加了更靈活的介面型別,包括屬性、函式、可索引和類等。
屬性介面
interface InterfaceName {
first: string;
second?: string; //加個問號,介面屬性就可以變成可傳可不傳了,不傳預設是undefined。
}
//列印變數
function logParam(name: InterfaceName): void {
console.log(name.first, name.second, 11);
}
//定義引數
const obj = { first: "1", second: "fff", three: 1 };
//logParam({ first: "1", second: "1", three: 1 }); //報錯,只能傳介面定義的值
logParam(obj);
tips: 用個變數來儲存傳入的變數,這樣可以傳入定義的介面以外的值,否則如果直接傳入物件中無介面定義的值會報錯,所以建議介面定義了哪些值就傳哪些值。
函式型別介面
對方法傳入的引數型別,以及返回值型別進行約束,可批量進行約束。
interface keyMap {
(key: string, value: string): string;
}
let logKeyMap: keyMap = function (key1: string, value: string): string {
return key1 + value;
};
console.log(logKeyMap("key1", "value"));
tips: 介面只對傳入的引數的型別和引數的個數進行約束,不對引數名稱進行約束。
可索引介面
- 約束陣列
interface Arr {
[index: number]: string;
}
let ss: Arr = ["2121"];
- 約束物件
interface Obj {
[index: string]: string;
}
let interfaceArr: Obj = { aa: "1" };
tips:
- 對
陣列
進行約束,index
後必須跟著number
型別。- 對
物件
進行約束,index
後必須跟著string
型別- 索引簽名引數型別必須為 "string" 或 "number"
類型別介面
- 對
類
進行約束,類似抽象類
的實現。
interface Animals {
name: string;
eat(): void;
}
class Dogs implements Animals {
name: string;
constructor(name: string) {
this.name = name;
}
eat() {}
}
- 介面繼承--介面可以繼承介面
interface Dog {
eat(): void;
}
interface Persons extends Dog {
work(): void;
}
class Cat {
code() {
console.log("貓在敲程式碼");
}
}
//可繼承類後再實現介面
class SuperMan extends Cat implements Persons {
eat(): void {
console.log(1);
}
work(): void {
console.log(2);
}
}
let superMan = new SuperMan();
superMan.code();
tips: 類介面會對類的屬性
和方法
進行約束,類似非抽象類繼承抽象類時必須實現某些方法和屬性,但對屬性和方法的型別的約束更加嚴格,除了方法void型別
可被重新定義
外,其他屬性或方法的型別定義需要和介面保持一致。
泛型
軟體工程中,我們不僅要建立一致的定義良好的api,同時也要考慮可重用性。
元件不僅能夠支援當前的資料型別,同時也能支援未來的資料型別,這在建立大型系統時為你提供了十分靈活的功能
泛型就是解決類
、介面
、方法
的複用性
,以及對不特定資料型別
的支援。
要求:傳入的引數和返回的引數一致
函式的泛型
function getDate<T>(value: T): T {
return value;
}
console.log(getDate<number>(123));
tips: 這裡的T
可改成其他任意值但定義的值,和傳入的引數以及返回的引數是一樣的,一般預設寫法是T,也是業內規範的選擇。
類的泛型
class MinClass<T> {
public list: T[] = [];
//新增
add(value: T): void {
this.list.push(value);
}
//求最小值
min(): T {
//假設這個值最小
let minNum = this.list[0];
for (let i = 0; i < this.list.length; i++) {
//比較並獲取最小值
minNum = minNum < this.list[i] ? minNum : this.list[i];
}
return minNum;
}
}
//例項化類 並且指定了類的T的型別是number
let minClass = new MinClass<number>();
minClass.add(23);
minClass.add(5);
minClass.add(2);
console.log(minClass.min());
//例項化類 並且指定了類的T的型別是string,則其方法的傳參和返回都是string型別
let minClass2 = new MinClass<string>();
minClass2.add("23");
minClass2.add("5");
minClass2.add("2");
console.log(minClass2.min());
介面的泛型
- 第一種寫法
interface ConfigFn {
//規範引數型別,返回值型別
<T>(value: T): T;
}
let getData: ConfigFn = function <T>(value: T): T {
return value;
};
console.log(getData<string>("z11"));
- 第二種寫法
interface ConfigFn<T> {
//引數型別 ,返回值型別
(value: T): T;
}
//介面方法
function getData<T>(value: T): T {
return value;
}
//使用介面
let myGetDate: ConfigFn<string> = getData;
console.log(myGetDate("3"));
tips:介面的泛型只針對函式型別的介面
類當做引數傳入泛型類
//使用者類--和資料庫表欄位進行對映
class User {
username: string | undefined;
password: string | undefined;
//建構函式-初始化引數
constructor(param: {
username: string | undefined;
password?: string | undefined;
}) {
this.username = param.username;
this.password = param.password;
}
}
//資料庫類
class Db<T> {
add(user: T): boolean {
console.log(user);
return true;
}
updated(user: T, id: number): boolean {
console.log(user, id);
return true;
}
}
let u = new User({
username: "張三",
});
//u.username = "李四";
u.password = "111111";
let db = new Db<User>();
db.add(u);
db.updated(u, 1);
tips: 類的引數名和型別都做了約束。
模組
內部模組稱為名稱空間,外部模組簡稱為模組,模組在其自身的作用域裡執行,而不是在全域性作用域裡;
這意味著定義在一個模組裡的變數、函式、類等等在模組外部是不可見的,除非你明確的使用export
形式之一匯出它們。
相反,如果想使用其它模組匯出的變數,函式,類,介面等的時候,你必須要導人它們,可以使用import
形式之一。
我們可以一些公共的功能單獨抽離成一個檔案作為一個模組。
模組裡面的變數、函式、類等預設是私有的,如果我們要在外部訪問模組裡面的資料(變數、函式、類)
我們需要通過export
暴露模組裡面的資料(變數、函式、類...)。
暴露後我們通過import
引入模組就可以使用模組裡面暴露的資料(變數、函式、類...)
//modules/db.ts
function getDate(): any[] {
console.log("獲取資料");
return [
{
userName: "張三",
},
{
userName: "李四",
},
];
}
//一個模組裡面可以用多次
// export { getDate };
//一個模組裡面只能用一次
export default getDate;
import { getDate as getDbDate } from "./modules/db";
import getDbDate from "./modules/db";
getDbDate();
tips: 這個除錯時瀏覽器中不能直接使用,可在node
和weakpack
的環境中除錯。
名稱空間
在程式碼量較大的情況下,為了避免各種變數命名相沖突,可將相似功能的函式、類、介面等放置到名稱空間內
TypeScript的名稱空間可以將程式碼包裹起來,只對外暴露需要在外部訪問的物件。
名稱空間和模組的區別
- 名稱空間:內部模組,主要用於組織程式碼,避免命名衝突。
- 模組:ts外部模組的簡稱,側重程式碼的複用,一個模組裡可能會有多個名稱空間。
// modules/Animal.ts
export namespace A {
interface Animal {
name: String;
eat(): void;
}
export class Dog implements Animal {
name: String;
constructor(theName: string) {
this.name = theName;
}
eat() {
console.log("我是" + this.name);
}
}
}
export namespace B {
interface Animal {
name: String;
eat(): void;
}
export class Dog implements Animal {
name: String;
constructor(theName: string) {
this.name = theName;
}
eat() {}
}
}
import { A, B } from "./modules/Animal";
let ee = new A.Dog("小貝");
ee.eat();
裝飾器
- 類裝飾器:類裝飾器在類申明之前被申明(緊靠著類申明),類裝飾器應用於類建構函式,可以用於監視,修改或者替換類定義。
function logClass(params: any) {
console.log(params);
//params 就是指代當前類--HttpClient
params.prototype.apiUrl = "動態擴充套件屬性";
params.prototype.run = function () {
console.log("動態擴充套件方法");
};
params.prototype.getDate = function () {
console.log("動態擴充套件方法2");
};
}
@logClass
class HttpClient {
constructor() {}
getDate() {
console.log(1);
}
}
let http: any = new HttpClient();
console.log(http.apiUrl);
http.run();
http.getDate();
tips: 裝飾器會覆蓋被裝飾的類中的方法。
- 裝飾器工廠
可傳參的裝飾器
function logClassB(param: string) {
return function (target: any) {
console.log(target, "裝飾器以下的類");
console.log(param, "裝飾器傳進來的屬性");
};
}
@logClassB("小慧")
class HttpClients {
constructor() {}
getDate() {}
}
let https: any = new HttpClients();
console.log(https);
- 建構函式裝飾器
function logClassC(target: any) {
console.log(target, 1111);
//用在這裡繼承目標類並過載方法和屬性
return class extends target {
a: any = "我是修改後的屬性";
getDate() {
console.log(this.a + "--裝飾器中的方法輸出的");
}
};
}
@logClassC
class HttpClient2 {
public a: string | undefined;
constructor() {
this.a = "我是建構函式裡面的a";
}
getDate() {
console.log(this.a);
}
}
const https2 = new HttpClient2();
https2.getDate();
未完待續~
下期預告:在vue中使用ts。