<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Typescript 使用日志(干貨)

          共 9767字,需瀏覽 20分鐘

           ·

          2020-09-02 21:31

          Typescript 使用日志

          最近這兩年,有很多人都在討論 Typescript,無論是社區(qū)還是各種文章都能看出來,整體來說正面的信息是大于負(fù)面的,這篇文章就來整理一下我所了解的 Typescript。

          本文主要分為 3 個(gè)部分:

          ?Typescript 基本概念?Typescript 高級(jí)用法?Typescript 總結(jié)

          Typescript 基本概念

          至于官網(wǎng)的定義,這里就不多做解釋了,大家可以去官網(wǎng)查看。Typescript 設(shè)計(jì)目標(biāo)[1]

          我理解的定義:賦予 Javascript 類型的概念,讓代碼可以在運(yùn)行前就能發(fā)現(xiàn)問題。

          Typescript 都有哪些類型

          1、Typescript 基本類型,也就是可以被直接使用的單一類型。

          ?數(shù)字?字符串?布爾類型?null?undefined?any?unknown?void?object?枚舉?never

          2、復(fù)合類型,包含多個(gè)單一類型的類型。

          ?數(shù)組類型?元組類型?字面量類型?接口類型

          3、如果一個(gè)類型不能滿足要求怎么辦?

          ?可空類型,默認(rèn)任何類型都可以被賦值成 null 或 undefined。?聯(lián)合類型,不確定類型是哪個(gè),但能提供幾種選擇,如:type1 | type2。?交叉類型,必須滿足多個(gè)類型的組合,如:type1 & type2。

          類型都在哪里使用

          在 Typescript 中,類型通常在以下幾種情況下使用。

          ?變量中使用?類中使用?接口中使用?函數(shù)中使用

          類型在變量中使用

          在變量中使用時(shí),直接在變量后面加上類型即可。

          let a: number;let b: string;let c: null;let d: undefined;let e: boolean;let obj: Ixxx = {  a: 1,  b: 2,};let fun: Iyyy = () => {};

          類型在類中使用

          在類中使用方式和在變量中類似,只是提供了一些專門為類設(shè)計(jì)的靜態(tài)屬性、靜態(tài)方法、成員屬性、構(gòu)造函數(shù)中的類型等。

          class Greeter {    static name:string = 'Greeter'    static log(){console.log(‘log')}    greeting: string;    constructor(message: string) {        this.greeting = message;    }    greet() {        return "Hello, " + this.greeting;    }}let greeter = new Greeter("world");

          類型在接口中使用

          在接口中使用也比較簡(jiǎn)單,可以理解為組合多個(gè)單一類型。

          interface IData {  name: string;  age: number;  func: (s: string) => void;}

          類型在函數(shù)中使用

          在函數(shù)中使用類型時(shí),主要用于處理函數(shù)參數(shù)、函數(shù)返回值。

          // 函數(shù)參數(shù)function a(all: string) {}// 函數(shù)返回值function a(a: string): string {}// 可選參數(shù)function a(a: number, b?: number) {}

          Typescript 高級(jí)用法

          Typescript 中的基本用法非常簡(jiǎn)單,有 js 基礎(chǔ)的同學(xué)很快就能上手,接下來我們分析一下 Typescript 中更高級(jí)的用法,以完成更精密的類型檢查。

          類中的高級(jí)用法

          在類中的高級(jí)用法主要有以下幾點(diǎn):

          ?繼承?存儲(chǔ)器 get set?readonly 修飾符?公有,私有,受保護(hù)的修飾符?抽象類 abstract

          繼承和存儲(chǔ)器和 ES6 里的功能是一致的,這里就不多說了,主要說一下類的修飾符和抽象類。

          類中的修飾符是體現(xiàn)面向?qū)ο蠓庋b性的主要手段,類中的屬性和方法在被不同修飾符修飾之后,就有了不同權(quán)限的劃分,例如:

          ?public 表示在當(dāng)前類、子類、實(shí)例中都能訪問。?protected 表示只能在當(dāng)前類、子類中訪問。?private 表示只能在當(dāng)前類訪問。

          class Animal {  // 公有,私有,受保護(hù)的修飾符  protected AnimalName: string;  readonly age: number;  static type: string;  private _age: number;  // 屬性存儲(chǔ)器  get age(): number {    return this._age;  }  set age(age: number) {    this._age = age;  }  run() {    console.log("run", this.AnimalName, this.age);  }  constructor(theName: string) {    this.AnimalName = theName;  }}Animal.type = "2"; // 靜態(tài)屬性const dog = new Animal("dog");dog.age = 2; // 給 readonly 屬性賦值會(huì)報(bào)錯(cuò)dog.AnimalName; // 實(shí)例中訪問 protected 報(bào)錯(cuò)dog.run; // 正常

          在類中的繼承也十分簡(jiǎn)單,和 ES6 的語法是一樣的。

          class Cat extends Animal {  dump() {    console.log(this.AnimalName);  }}let cat = new Cat("catname");
          cat.AnimalName; // 受保護(hù)的對(duì)象,報(bào)錯(cuò)cat.run; // 正常cat.age = 2; // 正常

          在面向?qū)ο笾校幸粋€(gè)比較重要的概念就是抽象類,抽象類用于類的抽象,可以定義一些類的公共屬性、公共方法,讓繼承的子類去實(shí)現(xiàn),也可以自己實(shí)現(xiàn)。

          抽象類有以下兩個(gè)特點(diǎn)。

          ?抽象類不能直接實(shí)例化?抽象類中的抽象屬性和方法,必須被子類實(shí)現(xiàn)

          tip 經(jīng)典問題:抽象類的接口的區(qū)別

          ?抽象類要被子類繼承,接口要被類實(shí)現(xiàn)。?在 ts 中使用 extends 去繼承一個(gè)抽象類。?在 ts 中使用 implements 去實(shí)現(xiàn)一個(gè)接口。?接口只能做方法聲明,抽象類中可以作方法聲明,也可以做方法實(shí)現(xiàn)。?抽象類是有規(guī)律的,抽離的是一個(gè)類別的公共部分,而接口只是對(duì)相同屬性和方法的抽象,屬性和方法可以無任何關(guān)聯(lián)。

          抽象類的用法如下。

          abstract class Animal {  abstract makeSound(): void;  // 直接定義方法實(shí)例  move(): void {    console.log("roaming the earch...");  }}class Cat extends Animal {  makeSound() {} // 必須實(shí)現(xiàn)的抽象方法  move() {    console.log('move');  }}new Cat3();

          接口中的高級(jí)用法

          接口中的高級(jí)用法主要有以下幾點(diǎn):

          ?繼承?可選屬性?只讀屬性?索引類型:字符串和數(shù)字?函數(shù)類型接口?給類添加類型,構(gòu)造函數(shù)類型

          接口中除了可以定義常規(guī)屬性之外,還可以定義可選屬性、索引類型等。

          interface Ia {  a: string;  b?: string; // 可選屬性  readonly c: number; // 只讀屬性  [key: number]: string; // 索引類型}// 接口繼承interface Ib extends Ia {  age: number;}let test1: Ia = {  a: "",  c: 2,  age: 1,};test1.c = 2; // 報(bào)錯(cuò),只讀屬性const item0 = test1[0]; // 索引類型

          接口中同時(shí)也支持定義函數(shù)類型、構(gòu)造函數(shù)類型。

          // 接口定義函數(shù)類型interface SearchFunc {  (source: string, subString: string): boolean;}let mySearch: SearchFunc = function (x: string, y: string) {  return false;};// 接口中編寫類的構(gòu)造函數(shù)類型檢查interface IClass {  new (hour: number, minute: number);}let test2: IClass = class {  constructor(x: number, y: number) {}};

          函數(shù)中的高級(jí)用法

          函數(shù)中的高級(jí)用法主要有以下幾點(diǎn):

          ?函數(shù)重載?this 類型

          函數(shù)重載

          函數(shù)重載指的是一個(gè)函數(shù)可以根據(jù)不同的入?yún)⑵ヅ鋵?duì)應(yīng)的類型。

          例如:案例中的?doSomeThing?在傳一個(gè)參數(shù)的時(shí)候被提示為?number?類型,傳兩個(gè)參數(shù)的話,第一個(gè)參數(shù)就必須是?string?類型。

          // 函數(shù)重載function doSomeThing(x: string, y: number): string;function doSomeThing(x: number): string;function doSomeThing(x): any {}
          let result = doSomeThing(0);let result1 = doSomeThing("", 2);

          This 類型

          我們都知道,Javascript 中的 this 只有在運(yùn)行的時(shí)候,才能夠判斷,所以對(duì)于 Typescript 來說是很難做靜態(tài)判斷的,對(duì)此 Typescript 給我們提供了手動(dòng)綁定 this 類型,讓我們能夠在明確 this 的情況下,給到靜態(tài)的類型提示。

          其實(shí)在 Javascript 中的 this,就只有這五種情況:

          ?對(duì)象調(diào)用,指向調(diào)用的對(duì)象?全局函數(shù)調(diào)用,指向 window 對(duì)象?call apply 調(diào)用,指向綁定的對(duì)象?dom.addEventListener 調(diào)用,指向 dom?箭頭函數(shù)中的 this ,指向綁定時(shí)的上下文

          // 全局函數(shù)調(diào)用 - windowfunction doSomeThing() {  return this;}const result2 = doSomeThing();
          // 對(duì)象調(diào)用 - 對(duì)象interface IObj { age: number; // 手動(dòng)指定 this 類型 doSomeThing(this: IObj): IObj; doSomeThing2(): Function;}
          const obj: IObj = { age: 12, doSomeThing: function () { return this; }, doSomeThing2: () => { console.log(this); },};const result3 = obj.doSomeThing();let globalDoSomeThing = obj.doSomeThing;globalDoSomeThing(); // 這樣會(huì)報(bào)錯(cuò),因?yàn)槲覀冎辉试S在對(duì)象中調(diào)用
          // call apply 綁定對(duì)應(yīng)的對(duì)象function fn() { console.log(this);}fn.bind(document)();
          // dom.addEventListenerdocument.body.addEventListener("click", function () { console.log(this); // body});

          泛型

          泛型表示的是一個(gè)類型在定義時(shí)并不確定,需要在調(diào)用的時(shí)候才能確定的類型,主要包含以下幾個(gè)知識(shí)點(diǎn):

          ?泛型函數(shù)?泛型類?泛型約束 T extends XXX

          我們?cè)囅胍幌拢绻粋€(gè)函數(shù),把傳入的參數(shù)直接輸出,我們?cè)趺慈ソo它編寫類型?傳入的參數(shù)可以是任何類型,難道我們需要把每個(gè)類型都寫一遍?

          ?使用函數(shù)重載,得把每個(gè)類型都寫一遍,不適合。?泛型,用一個(gè)類型占位 T 去代替,在使用時(shí)指定對(duì)應(yīng)的類型即可。

          // 使用泛型function doSomeThing(param: T): T {  return param;}
          let y = doSomeThing(1);
          // 泛型類class MyClass { log(msg: T) { return msg; }}
          let my = new MyClass();my.log("");
          // 泛型約束,可以規(guī)定最終執(zhí)行時(shí),只能是哪些類型function d2(param: T): T { return param;}let z = d2(true);

          其實(shí)泛型本來很簡(jiǎn)單,但許多初學(xué) Typescript 的同學(xué)覺得泛型很難,其實(shí)是因?yàn)榉盒涂梢越Y(jié)合索引查詢符?keyof、索引訪問符?T[k]?等寫出難以閱讀的代碼,我們來看一下。

          // 以下四種方法,表達(dá)的含義是一致的,都是把對(duì)象中的某一個(gè)屬性的 value 取出來,組成一個(gè)數(shù)組function showKey1(items: K[], obj: T): T[K][] {  return items.map((item) => obj[item]);}
          function showKey2(items: K[], obj: T): Array { return items.map((item) => obj[item]);}
          function showKey3( items: K[], obj: { [K in keyof T]: any }): T[K][] { return items.map((item) => obj[item]);}
          function showKey4( items: K[], obj: { [K in keyof T]: any }): Array { return items.map((item) => obj[item]);}
          let obj22 = showKey4<"age", { name: string; age: number }>(["age"], { name: "yhl", age: 12,});

          類型兼容性

          類型兼容性是我認(rèn)為 Typescript 中最難理解的一個(gè)部分,我們來分析一下。

          ?對(duì)象中的兼容?函數(shù)返回值兼容?函數(shù)參數(shù)列表兼容?函數(shù)參數(shù)結(jié)構(gòu)兼容?類中的兼容?泛型中的兼容

          在 Typescript 中是通過結(jié)構(gòu)體來判斷兼容性的,如果兩個(gè)的結(jié)構(gòu)體一致,就直接兼容了,但如果不一致,Typescript 給我們提供了一下兩種兼容方式:

          以?A = B?這個(gè)表達(dá)式為例:

          ?協(xié)變,表示 B 的結(jié)構(gòu)體必須包含 A 中的所有結(jié)構(gòu),即:B 中的屬性可以比 A 多,但不能少。?逆變,和協(xié)變相反,即:B 中的所有屬性都在 A 中能找到,可以比 A 的少。?雙向協(xié)變,即沒有規(guī)則,B 中的屬性可以比 A 多,也可以比 A 少。

          對(duì)象中的兼容

          對(duì)象中的兼容,采用的是協(xié)變。

          let obj1 = {  a: 1,  b: "b",  c: true,};
          let obj2 = { a: 1,};
          obj2 = obj1;obj1 = obj2; // 報(bào)錯(cuò),因?yàn)?obj2 屬性不夠

          函數(shù)返回值兼容

          函數(shù)返回值中的兼容,采用的是協(xié)變。

          let fun1 = function (): { a: number; b: string } {  return { a: 1, b: "" };};let fun2 = function (): { a: number } {  return { a: 1 };};
          fun1 = fun2; // 報(bào)錯(cuò),fun2 中沒有 b 參數(shù)fun2 = fun1;

          函數(shù)參數(shù)個(gè)數(shù)兼容

          函數(shù)參數(shù)個(gè)數(shù)的兼容,采用的是逆變。

          // 如果函數(shù)中的所有參數(shù),都可以在賦值目標(biāo)中找到,就能賦值let fun1 = function (a: number, b: string) {};let fun2 = function (a: number) {};
          fun1 = fun2;fun2 = fun1; // 報(bào)錯(cuò), fun1 中的 b 參數(shù)不能再 fun2 中找到

          函數(shù)參數(shù)兼容

          函數(shù)參數(shù)兼容,采用的是雙向協(xié)變。

          let fn1 = (a: { name: string; age: number }) => {  console.log("使用 name 和 age");};let fn2 = (a: { name: string }) => {  console.log("使用 name");};
          fn2 = fn1; // 正常fn1 = fn2; // 正常

          理解函數(shù)參數(shù)雙向協(xié)變

          1、我們思考一下,一個(gè)函數(shù)?dog => dog,它的子函數(shù)是什么?

          注意:原函數(shù)如果被修改成了另一個(gè)函數(shù),但他的類型是不會(huì)改變的,ts 還是會(huì)按照原函數(shù)的類型去做類型檢查!

          ?grayDog => grayDog

          ? ? ?不對(duì),如果傳了其他類型的 dog,沒有 grayDog 的方法,會(huì)報(bào)錯(cuò)。

          ? ? ?grayDog => animal

          ? ?同上。

          ? ? ?animal => animal

          ? ?返回值不對(duì),返回值始終是協(xié)變的,必須多傳。

          ? ? ?animal => grayDog

          ? ?正確。





          所以,函數(shù)參數(shù)類型應(yīng)該是逆變的。

          2、為什么 Typescript 中的函數(shù)參數(shù)也是協(xié)變呢?

          enum EventType { Mouse, Keyboard }interface Event { timestamp: number; }interface MouseEvent extends Event { x: number; y: number }
          function listenEvent(eventType: EventType, handler: (n: Event) => void) { /* ... */}listenEvent(EventType.Mouse, (e: MouseEvent) => console.log(e.x + "," + e.y));

          上面代碼中,我們?cè)谡{(diào)用時(shí)傳的是 mouse 類型,所以在回調(diào)函數(shù)中,我們是知道返回的參數(shù)一定是一個(gè) MouseEvent 類型,這樣是符合邏輯的,但由于 MouseEvent 類型的屬性是多于 Event 類型的,所以說 Typescript 的參數(shù)類型也是支持協(xié)變的。

          類中的兼容

          類中的兼容,是在比較兩個(gè)實(shí)例中的結(jié)構(gòu)體,是一種協(xié)變。

          class Student1 {  name: string;  // private weight:number}
          class Student2 { // extends Student1 name: string; age: number;}
          let student1 = new Student1();let student2 = new Student2();
          student1 = student2;student2 = student1; // 報(bào)錯(cuò),student1 沒有 age 參數(shù)

          需要注意的是,實(shí)例中的屬性和方法會(huì)受到類中修飾符的影響,如果是 private 修飾符,那么必須保證兩者之間的 private 修飾的屬性來自同一對(duì)象。如上文中如果把 private 注釋放開的話,只能通過繼承去實(shí)現(xiàn)兼容。

          泛型中的兼容

          泛型中的兼容,如果沒有用到 T,則兩個(gè)泛型也是兼容的。

          interface Empty {}let x1: Empty;let y1: Empty;
          x1 = y1;y1 = x1;

          高級(jí)類型

          Typescript 中的高級(jí)類型包括:交叉類型、聯(lián)合類型、字面量類型、索引類型、映射類型等,這里我們主要討論一下

          ?聯(lián)合類型?映射類型

          聯(lián)合類型

          聯(lián)合類型是指一個(gè)對(duì)象可能是多個(gè)類型中的一個(gè),如:let a :number | string?表示 a 要么是 number 類型,要么是 string 類型。

          那么問題來了,我們?cè)趺慈ゴ_定運(yùn)行時(shí)到底是什么類型?

          答:類型保護(hù)。類型保護(hù)是針對(duì)于聯(lián)合類型,讓我們能夠通過邏輯判斷,確定最終的類型,是來自聯(lián)合類型中的哪個(gè)類型。

          判斷聯(lián)合類型的方法很多:

          ?typeof?instanceof?in?字面量保護(hù),===!=====!=?自定義類型保護(hù),通過判斷是否有某個(gè)屬性等

          // 自定義類型保護(hù)function isFish(pet: Fish | Bird): pet is Fish {  return (pet).swim !== undefined;}if (isFish(pet)) {  pet.swim();} else {  pet.fly();}

          映射類型

          映射類型表示可以對(duì)某一個(gè)類型進(jìn)行操作,產(chǎn)生出另一個(gè)符合我們要求的類型:

          ?ReadOnly,將 T 中的類型都變?yōu)橹蛔x。?Partial,將 T 中的類型都變?yōu)榭蛇x。?Exclude,從 T 中剔除可以賦值給 U 的類型。?Extract,提取 T 中可以賦值給 U 的類型。?NonNullable,從 T 中剔除 null 和 undefined。?ReturnType,獲取函數(shù)返回值類型。?InstanceType,獲取構(gòu)造函數(shù)類型的實(shí)例類型。

          我們也可以編寫自定義的映射類型。

          //定義toPromise映射type ToPromise = { [K in keyof T]: Promise };type NumberList = [number, number];type PromiseCoordinate = ToPromise;// [Promise, Promise]

          Typescript 總結(jié)

          寫了這么多,接下來說說我對(duì) Typescript 的一些看法。

          Typescript 優(yōu)點(diǎn)

          1、靜態(tài)類型檢查,提早發(fā)現(xiàn)問題。

          2、類型即文檔,便于理解,協(xié)作。

          3、類型推導(dǎo),自動(dòng)補(bǔ)全,提升開發(fā)效率。

          4、出錯(cuò)時(shí),可以大概率排除類型問題,縮短 bug 解決時(shí)間。

          實(shí)戰(zhàn)中的優(yōu)點(diǎn):

          1、發(fā)現(xiàn) es 規(guī)范中棄用的方法,如:Date.toGMTString。

          2、避免了一些不友好的開發(fā)代碼,如:動(dòng)態(tài)給 obj 添加屬性。

          3、vue 使用變量,如果沒有在 data 定義,會(huì)直接拋出問題。

          Typescript 缺點(diǎn)

          1、短期增加開發(fā)成本。

          2、部分庫還沒有寫 types 文件。

          3、不是完全的超集。

          實(shí)戰(zhàn)中的問題:

          1、還有一些坑不好解決,axios 編寫了攔截器之后,typescript 反映不到 response 中去。

          參考資料

          ?Typescript 官網(wǎng)[2]?深入理解 Typescript[3]

          References

          [1]?Typescript 設(shè)計(jì)目標(biāo):?https://github.com/Microsoft/TypeScript/wiki/TypeScript-Design-Goals
          [2]?Typescript 官網(wǎng):?https://www.tslang.cn/
          [3]?深入理解 Typescript:?https://jkchao.github.io/typescript-book-chinese/

          學(xué)習(xí)交流

          • 關(guān)注公眾號(hào)【前端宇宙】,每日獲取好文推薦
          • 添加微信,入群交流

          “在看和轉(zhuǎn)發(fā)”就是最大的支持
          瀏覽 62
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  中文字幕在线观看一区 | 成人免费a级 | 先锋影音av在线 亚州在线无码视频 | 亚洲乱码一区二区三区 | 97男人的天堂 |