TS進(jìn)階知識(shí)

一:TypeScript的配置文件:

https://blog.csdn.net/weixin_46221198/article/details/125589781
√ 標(biāo)記為常用配置
常用配置

"include": ["./src/*"],//src目錄下的編譯為js,,支持支持 glob 模式匹配√
//"files":["a.ts"],//指定具體的文件(只能是文件),需編譯的文件較少時(shí)使用√
"exclude":["./data/*"]//data目錄下的不不便宜為js√
"compilerOptions": {
  "incremental": true, // TS編譯器在第一次編譯之后會(huì)生成一個(gè)存儲(chǔ)編譯信息的文件,第二次編譯會(huì)在第一次的基礎(chǔ)上進(jìn)行增量編譯,可以提高編譯的速度 √(增量編譯)
  "tsBuildInfoFile": "./buildFile", // 增量編譯文件的存儲(chǔ)位置
  "diagnostics": true, // 打印診斷信息 
  "target": "ES5", // 目標(biāo)語言的版本
  "module": "CommonJS", // 生成代碼的模板標(biāo)準(zhǔn)
  "outFile": "./app.js", // 將多個(gè)相互依賴的文件生成一個(gè)文件,可以用在AMD模塊中,即開啟時(shí)應(yīng)設(shè)置"module": "AMD",
  "lib": ["DOM", "ES2015", "ScriptHost", "ES2019.Array"], // TS需要引用的庫,即聲明文件,es5 默認(rèn)引用dom、es5、scripthost,如需要使用es的高級版本特性,通常都需要配置,如es8的數(shù)組新特性需要引入"ES2019.Array",
  "allowJS": true, // 允許編譯器編譯JS,JSX文件 √
  "checkJs": true, // 允許在JS文件中報(bào)錯(cuò),通常與allowJS一起使用
  "outDir": "./dist", // 指定輸出目錄 √
  "rootDir": "./", // 指定輸出文件目錄(用于輸出),用于控制輸出目錄結(jié)構(gòu) √
  "declaration": true, // 生成聲明文件,開啟后會(huì)自動(dòng)生成聲明文件
  "declarationDir": "./file", // 指定生成聲明文件存放目錄
  "emitDeclarationOnly": true, // 只生成聲明文件,而不會(huì)生成js文件
  "sourceMap": true, // 生成目標(biāo)文件的sourceMap文件
  "inlineSourceMap": true, // 生成目標(biāo)文件的inline SourceMap,inline SourceMap會(huì)包含在生成的js文件中
  "declarationMap": true, // 為聲明文件生成sourceMap
  "typeRoots": [], // 聲明文件目錄,默認(rèn)時(shí)node_modules/@types
  "types": [], // 加載的聲明文件包
  "removeComments":true, // 刪除注釋 √
  "noEmit": true, // 不輸出文件,即編譯后不會(huì)生成任何js文件
  "noEmitOnError": true, // 發(fā)送錯(cuò)誤時(shí)不輸出任何文件
  "noEmitHelpers": true, // 不生成helper函數(shù),減小體積,需要額外安裝,常配合importHelpers一起使用
  "importHelpers": true, // 通過tslib引入helper函數(shù),文件必須是模塊
  "downlevelIteration": true, // 降級遍歷器實(shí)現(xiàn),如果目標(biāo)源是es3/5,那么遍歷器會(huì)有降級的實(shí)現(xiàn)
  "strict": true, // 開啟所有嚴(yán)格的類型檢查 √
  "alwaysStrict": true, // 在代碼中注入'use strict' √
  "noImplicitAny": true, // 不允許隱式的any類型 √
  "strictNullChecks": true, // 不允許把null、undefined賦值給其他類型的變量
  "strictFunctionTypes": true, // 不允許函數(shù)參數(shù)雙向協(xié)變
  "strictPropertyInitialization": true, // 類的實(shí)例屬性必須初始化
  "strictBindCallApply": true, // 嚴(yán)格的bind/call/apply檢查
  "noImplicitThis": true, // 不允許this有隱式的any類型
  "noUnusedLocals": true, // 檢查只聲明、未使用的局部變量(只提示不報(bào)錯(cuò))
  "noUnusedParameters": true, // 檢查未使用的函數(shù)參數(shù)(只提示不報(bào)錯(cuò))
  "noFallthroughCasesInSwitch": true, // 防止switch語句貫穿(即如果沒有break語句后面不會(huì)執(zhí)行)
  "noImplicitReturns": true, //每個(gè)分支都會(huì)有返回值
  "esModuleInterop": true, // 允許export=導(dǎo)出,由import from 導(dǎo)入
  "allowUmdGlobalAccess": true, // 允許在模塊中全局變量的方式訪問umd模塊
  "moduleResolution": "node", // 模塊解析策略,ts默認(rèn)用node的解析策略,即相對的方式導(dǎo)入
  "baseUrl": "./", // 解析非相對模塊的基地址,默認(rèn)是當(dāng)前目錄
  "paths": { // 路徑映射,相對于baseUrl
    // 如使用jq時(shí)不想使用默認(rèn)版本,而需要手動(dòng)指定版本,可進(jìn)行如下配置
    "jquery": ["node_modules/jquery/dist/jquery.min.js"]
  },
  "rootDirs": ["src","out"], // 將多個(gè)目錄放在一個(gè)虛擬目錄下,用于運(yùn)行時(shí),即編譯后引入文件的位置可能發(fā)生變化,這也設(shè)置可以虛擬src和out在同一個(gè)目錄下,不用再去改變路徑也不會(huì)報(bào)錯(cuò)
  "listEmittedFiles": true, // 打印輸出文件
  "listFiles": true// 打印編譯的文件(包括引用的聲明文件)
}

二:聯(lián)合類型和類型保護(hù)(類型斷言)

類型保護(hù):就是一些表達(dá)式或者關(guān)鍵字在編譯時(shí)候就能確定在某個(gè)作用域內(nèi)變量的類型.
類型斷言:通過類型斷言這種方式可以告訴編譯器,“相信我,我知道自己在干什么”。 類型斷言好比其它語言里的類型轉(zhuǎn)換,但是不進(jìn)行特殊的數(shù)據(jù)檢查和解構(gòu)。 它沒有運(yùn)行時(shí)的影響,只是在編譯階段起作用。 TypeScript 會(huì)假設(shè)你,程序員,已經(jīng)進(jìn)行了必須的檢查。

//聯(lián)合類型和類型保護(hù)
interface Bird {
  fly: boolean;
  sing: () => {};
}

interface Dog {
  fly: boolean;
  bark: () => {};
}


function trainAnimal(animal: Bird | Dog) {//聯(lián)合類型
  animal.sing();//報(bào)錯(cuò)
}
類型聯(lián)合報(bào)錯(cuò).png

這里不能確認(rèn)animal上是否有sing方法,這里我們需要進(jìn)行類型保護(hù)

//方法一:類型斷言
function trainAnimal(animal: Bird | Dog) {
  //聯(lián)合類型
  if (animal.fly) {
    (animal as Bird).sing();
  } else {
    (animal as Dog).bark();
  }
}
//方法二 in語法判斷的方式
function trainAnimalSecond(animal: Bird | Dog) {
  if ("sing" in animal) {
    animal.sing();
  } else {
    animal.bark();
  }
}
//instanceof 語法判斷
class Num {
  count: number;
  constructor(num: number) {
    this.count = num;
  }
}
//instanceof 語法判斷
function addSecond(first: object | Num, second: object | Num) {
  if (first instanceof Num && second instanceof Num) {
    return first.count + second.count;
  }
  return 0;
}

三:枚舉

枚舉:允許開發(fā)者定義一組命名的常量。使用枚舉可以使其更容易記錄意圖,或創(chuàng)建一組不同的情況。TypeScript提供了基于數(shù)字和字符串的枚舉。
1 數(shù)值型枚舉
我們首先從數(shù)字枚舉開始,如果你來自其他語言,可能會(huì)更熟悉它。一個(gè)枚舉可以用 enum 關(guān)鍵字來定義。

enum Direction {
  Up = 1, 
  Down, 
  Left, 
  Right,
}

上面,我們有一個(gè)數(shù)字枚舉,其中 Up 被初始化為 1 ,所有下面的成員從這一點(diǎn)開始自動(dòng)遞增。換句話說,Direction.Up的值是 1 ,Down 是 2,Left是3,Right是4。

如果我們愿意,我們可以完全不使用初始化器:

enum Direction {
  Up,
  Down,
  Left,
  Right,
}

這里,Up的值是0,Down是1,依次類推。這種自動(dòng)遞增的行為對于我們可能不關(guān)心成員值本身,但關(guān)心每個(gè)值與同一枚舉中的其他值不同的情況很有用。

使用枚舉很簡單:只需將任何成員作為枚舉本身的一個(gè)屬性來訪問,并使用枚舉的名稱來聲明類型:

enum UserResponse {
  No = 0,
  Yes = 1,
}
 
function respond(recipient: string, message: UserResponse): void {
  // ...
}
respond("Princess Caroline", UserResponse.Yes);

數(shù)字枚舉可以混合在計(jì)算和常量成員中(見下文)。簡而言之,沒有初始化器的枚舉要么需要放在第一位,要么必須放在用數(shù)字常量或其他常量枚舉成員初始化的數(shù)字枚舉之后。換句話說,下面的情況是不允許的:

enum E {
  A = getSomeValue(),  
  B,  
  // ? Enum成員必須有初始化器。
}

2 字符串枚舉
字符串枚舉是一個(gè)類似的概念,但有一些細(xì)微的運(yùn)行時(shí)差異,如下文所述。在一個(gè)字符串枚舉中,每個(gè)成員都必須用一個(gè)字符串字頭或另一個(gè)字符串枚舉成員進(jìn)行常量初始化。

enum Direction {
  Up = "UP", 
  Down = "DOWN", 
  Left = "LEFT", 
  Right = "RIGHT",
}

雖然字符串枚舉沒有自動(dòng)遞增的行為,但字符串枚舉有一個(gè)好處,那就是它們可以很好地 "序列化"。換句話說,如果你在調(diào)試時(shí)不得不讀取一個(gè)數(shù)字枚舉的運(yùn)行時(shí)值,這個(gè)值往往是不透明的--它本身并不傳達(dá)任何有用的意義(盡管 反向映射往往可以幫助你),字符串枚舉允許你在代碼運(yùn)行時(shí)給出一個(gè)有意義的、可讀的值,與枚舉成員本身的名稱無關(guān)。

四.泛型:generic泛指的類型,指的是在定義函數(shù)/接口/類型時(shí),不預(yù)先指定具體的類型,而是在使用的時(shí)候在指定類型限制的一種特性。

//函數(shù)中的泛型:限制入?yún)?返回值
function connect<Common>(first: Common, second: Common) {
  return `${first}${second}`;
}
connect<string>("1", "2");
connect<number>(1, 2);
function join<Common>(first: Common, second: Common):T {
  return first;
}

//------------------------------------------------------
//多個(gè)泛型
function map<T, P>(first: T, second: P) {
  return `${first}${second}`;
}
map<string, number>("1", 2);
map(1,2); //默認(rèn)類型推斷--<number,number>

類中的泛型:

// class DataManager {
//   constructor(
//     private item1: string[] | number[],
//     private item2: string[] | number[]
//   ) {} //類型聯(lián)合
//   getItem(index: number) {
//     return this.item1[index];
//   }
// }
//當(dāng)有大量的參數(shù)需要聯(lián)合類型時(shí),我們可以使用泛型來進(jìn)行代碼優(yōu)化

class DataManager<T, K> {
  constructor(private item1: T[], private item2: K[]) {}
  getItem(index: number): string {
    return `${this.item1[index]}${this.item2[index]}`;
  }
}
const dataManager = new DataManager<string, number>(["1"], [2]);


interface Test {
  name: string;
}

class Manager<T extends Test> {
  constructor(private item: T[]) {}
  getItem(index: number): string {
    return this.item[index].name;
  }
}

const manager = new Manager<Test>([{ name: "123" }]);


class Demo<T extends string | number>{
  constructor(private item: T[]) {}
  getItem(index: number): string|number {
    return this.item[index];
  }
}
const demo = new Demo(["123"]);
const demo1 = new Demo([123]);

泛型接口

 interface IBaseUser<T> {
    data: Array<T>;
    add: (t: T) => T;
    findUserById: (id: number) => T | undefined;
  }

  //用戶信息類
  class User {
    id?: number;
    name: string;
    age: number;
    constructor(name: string, age: number) {
      this.name = name;
      this.age = age;
    }
  }

  class UserCRUD implements IBaseUser<User> {
    data: Array<User> = [];
    add(user: User) {
      if (!user.id) {
        user.id = Date.now() + Math.random();
      }
      this.data.push(user);
      return user;
    }
    findUserById(id: number) {
      console.log("id:", id);
      return this.data.find((item) => item.id == id);
    }
  }
  const userCRUD = new UserCRUD();
  userCRUD.add(new User("張三", 20));
  const { id } = userCRUD.add(new User("李四", 21));
  userCRUD.add(new User("王五", 22));
  userCRUD.add(new User("趙六", 23));

  console.log(userCRUD);
  if (id) {
    console.log(userCRUD.findUserById(id));
  }

泛型約束:

如果我們直接對一個(gè)泛型參數(shù)取 length 屬性, 會(huì)報(bào)錯(cuò), 因?yàn)檫@個(gè)泛型根本就不知道它有這個(gè)屬性

// 沒有泛型約束
function fn <T>(x: T): void {
  // console.log(x.length)  // error
}
我們可以使用泛型約束來實(shí)現(xiàn)

interface Lengthwise {
  length: number;
}

// 指定泛型約束

function fn2 <T extends Lengthwise>(x: T): void {
  console.log(x.length)
}

我們需要傳入符合約束類型的值,必須包含必須 length 屬性:

fn2('abc')
// fn2(123) // error  number沒有l(wèi)ength屬性

五:namespace:類似于模塊化的概念,減少全局變量的產(chǎn)生,把一組相關(guān)的內(nèi)容封裝在一起.

class Header {
  constructor() {
    const ele = document.createElement("div");
    ele.innerText = "This is Header";
    document.body.appendChild(ele);
  }
}
class Content {
  constructor() {
    const ele = document.createElement("div");
    ele.innerText = "This is Content";
    document.body.appendChild(ele);
  }
}

class Footer {
  constructor() {
    const ele = document.createElement("div");
    ele.innerText = "This is Footer";
    document.body.appendChild(ele);
  }
}

class Page {
  constructor() {
    new Header();
    new Content();
    new Footer();
  }
}

上面的方式會(huì)產(chǎn)生大量的全局變量.png

我們可以通過namespace來進(jìn)行優(yōu)化:通過namespace來創(chuàng)建一個(gè)命名空間,通過export來將需要用到的變量進(jìn)行導(dǎo)出.

namespace Home {
  class Header {
    constructor() {
      const ele = document.createElement("div");
      ele.innerText = "This is Header";
      document.body.appendChild(ele);
    }
  }
  class Content {
    constructor() {
      const ele = document.createElement("div");
      ele.innerText = "This is Content";
      document.body.appendChild(ele);
    }
  }

  class Footer {
    constructor() {
      const ele = document.createElement("div");
      ele.innerText = "This is Footer";
      document.body.appendChild(ele);
    }
  }

  export class Page {
    constructor() {
      new Header();
      new Content();
      new Footer();
    }
  }
}
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <script src="./dist/test.js"></script>
  </head>
  <body>
    <script>
      new Home.Page();
    </script>
  </body>
</html>
優(yōu)化后我們只能訪問到Page.png

如果我們在多個(gè)地方引用Header Content Footer的話,再度優(yōu)化上面的代碼.

namespace Component {
  export class Header {
    constructor() {
      const ele = document.createElement("div");
      ele.innerText = "This is Header";
      document.body.appendChild(ele);
    }
  }
  export class Content {
    constructor() {
      const ele = document.createElement("div");
      ele.innerText = "This is Content";
      document.body.appendChild(ele);
    }
  }

  export class Footer {
    constructor() {
      const ele = document.createElement("div");
      ele.innerText = "This is Footer";
      document.body.appendChild(ele);
    }
  }
}

///<reference path="./components.ts"/>

namespace Home {
  export class Page {
    constructor() {
      new Component.Header();
      new Component.Content();
      new Component.Footer();
    }
  }
}

像上面這樣,我們可以通過引用components.ts對Component中的Header,Content,Footer進(jìn)行復(fù)用.

進(jìn)一步的優(yōu)化,通過模塊化來處理上面的問題, amd -- > import define

六. 類型定義文件:幫助Ts文件理解引入的Js文件/js庫里面的內(nèi)容.

因?yàn)镴s是中是TS中要求的類型的概念.(類型描述文件: .d.ts文件 )

以引入jquery為例:

//index.html
<!--
 * @Author: itw_liuchao04 itw_liuchao04@tkmail.com
 * @Date: 2022-10-26 13:54:30
 * @LastEditors: itw_liuchao04 itw_liuchao04@tkmail.com
 * @LastEditTime: 2022-10-26 13:57:52
 * @FilePath: \Ts\src\index.html
 * @Description: 
 * 
 * Copyright (c) 2022 by itw_liuchao04 itw_liuchao04@tkmail.com, All Rights Reserved. 
-->
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.1/jquery.js"></script>
    <script src="./page.js"></script>
  </head>
  <body></body>
</html>

//page.ts

$(function () {
  console.log("abc");
  $("body").html("<div>123</div>");
});

//jquery.d.ts

//定義全局變量
//declare var $: (params: () => void) => void;
//定義全局方法
declare function $(params: () => void): () => void;

interface JqueryInstance {
  html(param: string): () => void;
}

declare function $(seclotor: string): JqueryInstance;

//使用 interface語法,實(shí)現(xiàn)函數(shù)重載

interface JQuery {
  (params: () => void): void;
  (params: string): JqueryInstance;
}

declare var $: JQuery;

七:泛型中keyof語法的使用

interface Person {
  name: string;
  age: number;
  gender: string;
}
class Teacher {
  constructor(private info: Person) {}
  getInfo(key: string) {
    //類型保護(hù)
    if (key === "name" || key === "age" || key === "gender") {
      return this.info[key];
    }
    return "非法參數(shù)";
  }
}

let person = {
  name: "張三",
  age: 18,
  gender: "male",
};

const teacher = new Teacher(person);
console.log(teacher.getInfo("name"));//張三
console.log(teacher.getInfo("hobby"));//非法參數(shù)

使用keyof來替換上面的代碼實(shí)現(xiàn)

interface Person {
  name: string;
  age: number;
  gender: string;
}
class Teacher {
  constructor(private info: Person) {}
  getInfo<T extends keyof Person>(key: T) {
    //類型保護(hù)
    // if (key === "name" || key === "age" || key === "gender") {
    //   return this.info[key];
    // }
    return this.info[key];
  }
}

let person = {
  name: "張三",
  age: 18,
  gender: "male",
};

const teacher = new Teacher(person);
console.log(teacher.getInfo("name")); //張三
console.log(teacher.getInfo("hobby")); //Argument of type '"hobby"' is not assignable to parameter of type 'keyof Person'

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,406評論 6 538
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,034評論 3 423
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,413評論 0 382
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,449評論 1 316
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 72,165評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,559評論 1 325
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,606評論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,781評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,327評論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 41,084評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,278評論 1 371
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,849評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,495評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,927評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,172評論 1 291
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 52,010評論 3 396
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 48,241評論 2 375

推薦閱讀更多精彩內(nèi)容