TS珠峰 001【學習筆記】

課程大綱

  • 搭建 TypeScript 開發環境。
  • 掌握 TypeScript 的基礎類型,聯合類型和交叉類型。
  • 詳細類型斷言的作用和用法。
  • 掌握 TypeScript 中函數、類的類型聲明方式。
  • 掌握類型別名、接口的作用和定義。
  • 掌握泛型的應用場景,熟練應用泛型。
  • 靈活運用條件類型、映射類型與內置類型。
  • 創建和使用自定義類型。
  • 理解命名空間、模塊的概念已經使用場景。
  • 詳細 TS 中的類型保護、裝飾器。
  • 巧妙運用類型推導和化簡代碼。
  • 深入理解 TypeScript 類型層級系統。
  • 詳細函數的變與逆變。
  • 深入研究 infer 的用法與技巧。
  • 詳細探究符號類型。
  • 靈活編寫與運用類型聲明文件,擴展 TypeScript 的類型系統。
  • 掌握 TS 中類型文件查找規則。
  • 深刻使用裝飾器,運用反射元數據擴展裝飾器的功能,實現控制反轉、依賴注入。
  • 深度解析 TSConfig 配置文件。
  • TS 類型體操

ts基礎

目前大部分企業的中大型前端項目都採用了 Typescript,那麼為甚麼我們需要它?

JavaScript 的核心特點就是靈活,但隨著項目規模的增大,靈活反而增加開發者的心智負擔。例如在代碼中一個變量可以被賦予字符串、布爾、數字,甚至是函數,這樣就充滿了不確定性。而且這些不確定性可能需要在代碼運行的時候才能被發現,所以我們需要類型的約束。

當然不可否認的是有了類型的加持多少會影響開發效率,但是可以讓大型項目更加健壯

  • Typescript 更像後端 JAVA,讓 JS 可以開發大型企業應用;
  • TS 提供的類型系統可以幫助我們在寫代碼時提供豐富的語法提示;
  • 在編寫代碼時會對代碼進行類型檢查從而避免很多線上錯誤;

越來越多的項目開始擁抱 TS 了,典型的 Vue3、Pinia、第三方工具庫,後端 NodeJS 等。我們也經常為以上編程擁有了更好的支持去編寫 .d.ts 文件。

1. ts是一門語言

  1. ts 是 js 的一個超集,擴展了語法添加了靜態類型支持以及其他一些新特性

環境配置

全局安裝

npm install typescript -g
# 我們可以用tsc來把ts轉成js
# 我們要把甚麼轉換成甚麼,所以我們要先生成一個配置
# 在當前項目下創建一個配置文件tsc --init
# --watch
tsc --init
# 插件code runner這個插件 需要額外安裝一個ts-node的包
 npm init -y
 npm install typescript rollup -D
 npm install rollup-plugin-typescript -D
 npm i @rollup/plugin-node-resolve rollup-plugin-serve -D
#  rollup.config.js
# error lens 插件

學習ts就是學習類型,ts的類型分類(DOM,Promise,原始方法)基礎類型,
ts中:後面的都是類型 = 後面的都是值
ts 一切從安全的角度出發,看能不能賦值,就看安全不安全
ts 還有自動的類型推導,不用見到變量就寫類型,而是推斷的不正確,我們才需要自己編寫

基礎類型

let abc: string = 'Hello World';
console.log(abc);

let name = 'John Doe'; // 當前模塊中,作用域隔離
let age = 20;
let handsome: boolean = true;
// 原始類似標識都是小寫的,而類名都是大寫的它描述的實例(都是對象)
// 類型註解:告訴TS變量的類型
// 類型推斷:TS會自動嘗試分析變量的類型
// 類型註解優先級高於類型推斷
// 類型註解:變量名: 類型 = 值
// 類型推斷:變量名 = 值
let s1: string = 'Hello World';
let s2: String = new String('Hello World');
let s3: String = 'Hello World'; // 會自動轉換為對象
// 數組聲明
let arr1: number[] = [1, 2, 3];
let arr2: Array<number> = [1, 2, 3];
// 元組:固定長度的數組
let tuple: [string, number] = ['Hello', 123];
// 元組的越界問題
// tuple[2] = 'World'; // 會報錯
let tuple2: readonly [string, number, boolean] = ['Hello', 123, true];
// tuple2[0] = 'World'; // 會報錯
export { name };

枚舉(自帶類型的對象)

let abc: string = 'Hello World';
console.log(abc);

let name = 'John Doe'; // 當前模塊中,作用域隔離
let age = 20;
let handsome: boolean = true;
// 原始類似標識都是小寫的,而類名都是大寫的它描述的實例(都是對象)
// 類型註解:告訴TS變量的類型
// 類型推斷:TS會自動嘗試分析變量的類型
// 類型註解優先級高於類型推斷
// 類型註解:變量名: 類型 = 值
// 類型推斷:變量名 = 值
let s1: string = 'Hello World';
let s2: String = new String('Hello World');
let s3: String = 'Hello World'; // 會自動轉換為對象
// 數組聲明
let arr1: number[] = [1, 2, 3];
let arr2: Array<number> = [1, 2, 3];
// 元組:固定長度的數組
let tuple: [string, number] = ['Hello', 123];
// 元組的越界問題
// tuple[2] = 'World'; // 會報錯
let tuple2: readonly [string, number, boolean] = ['Hello', 123, true];
// tuple2[0] = 'World'; // 會報錯

// 枚舉 自帶類型的對象,自動增長
enum USER_ROLE {
  USER,
  ADMIN = 6,
  MANAGER,
  OTHER = 'other', // 異構枚舉
}
/*
  USER = 0,
  ADMIN = 6,
  MANAGER = 7,
*/
console.log(USER_ROLE.USER); // 0
// 如果不需要對象可以直接採用常量枚舉
const enum USER_ROLE2 {
  USER,
  ADMIN,
  MANAGER,
}
console.log(USER_ROLE2.USER); // 0
export { name };

null 和 undefined

// 嚴格模式下,不允許使用 any 類型
// nul 和 undefined 是任何類型的子類型
let x: number | null | undefined = 1;
// let y: number = undefined; // 會報錯
// let z: number = null; // 會報錯
// strict:false 可以使用 any 類型

never類型

永遠不

// never 類型 它是任何類型的子類型,也可以賦值給任何類型
// never 類型的變量只能被賦值為 never 類型
function fn1(): never {
  throw new Error('報錯了');
}
// 類型保護,保障程序的不缺失
// never 是永遠到達不了的終點
function fn2(): never {
  while (true) {}
}
function validate(x: never) {
  console.log(x);
}
// 針對不同的類型做不同的處理
function fn3(x: number | string | boolean) {
  // 類似有收斂的作用
  if (typeof x === 'number') {
    console.log(x.toFixed(2));
    return;
  }
  if (typeof x === 'string') {
    console.log(x.trim());
    return;
  }
  if (typeof x === 'boolean') {
    console.log(x.valueOf());
    return;
  }
  //守衛語句
  validate(x); // 如果類型不匹配,會報錯 這個邏輯應該是永遠不會執行的所以上在還是有沒覆蓋到的邏輯(要添加boolean類型的判斷)
}

object的類型

// object 類型 object {} Object
let obj: object = { name: 'John Doe' };
// obj.name = 'Jane Doe'; // 會報錯
// 小寫的 object 是對象類型,大寫的 Object 是對象的構造函數 

!號非空斷言

聯合類型

// 一般我們會基於額外的類型來擴展定義類型 有點類似枚舉
type Direction = "up" | "down" | "right" | "left"
let direction :Direction = "left"
// type和interface的區別
type women =
  | {
      wealthy: true;
      waste: string;
    }
  | {
      wealthy: false;
      norality: string;
    };
// 是富人一定不是節儉的人,是節儉的人一定不是富人

可以利用聯合類型來做到屬性之間的互斥

斷言

斷言有可能出問題,出問題後果自負

type women =
  | {
      wealthy: true;
      waste: string;
    }
  | {
      wealthy: false;
      norality: string;
    };
// 是富人一定不是節儉的人,是節儉的人一定不是富人(類比約定,不嚴謹)
// 斷言 把某個類型斷言為為已經存在的一種類型
let ele = document.getElementById('app');
ele!.innerHTML = 'Hello World'; // 繞過TS的類型檢查 ele?.innerHTML = 'Hello World'; // 可選鏈 操作符(取值)
// false || true = true
// false && true = false
// null ?? 'Hello World' = 'Hello World' // 空值合併操作符
// as 類型斷言 可以強制把某個類型斷言為另外一個類型 as 類型
// 雙重斷言,我們可以把一個值斷言成為 any 類型,然後再斷言成為另外一個類型
// 類型斷言不是類型轉換,只是告訴TS編譯器,這個值是甚麼類型
// 類型斷言只在編譯階段起作用,不會影響真實的值

函數

// 函數類型

// 函數聲明 function定義 函數表達式
function sum(a: number, b: number): number {
  return a + b;
}
let sum2 = function (a: number, b: number): number {
  return a + b;
};
let sum3: (a: number, b: number) => number = function (a, b) {
  return a + b;
};
// 類型比較長可以寫成下面
type Sum = (a: number, b: number) => number;
let sum4: Sum = function (a, b) {
  return a + b;
};
// 會根據上下文推導賦予值的類型
let sum5: Sum = (a, b) => a + b;

// 常見的類型推導的方式
// 從右向左 根據賦值進行推導
let name = "jianz"
let age = 20
// 根據返回值進行類型推導
// void 表示不關心返回的具體類型(不校驗)
// 可選參數(?)
let sum6 = (a: string = 'a', b?: string): string => {
  return a + b;
}; 

// 對象類型
let person = {
  name: 'John Doe',
  age: 20,
};
// ts中的this類型需要手動指定,默認是函數的第一個參數
function getVal(this: typeof person, key: keyof typeof person) {
  return this[key]; // this指向調用者,必須要有類型斷言才能使用{
}
let r = getVal.call(person, 'name');
console.log(r);

重載

// 重載 (一般是有限操作), 它只是一個偽重載,只是一個類型的重載
function toArray(value: number): number[];
function toArray(value: string): string[];
function toArray(value: number | string) {
  if (typeof value === 'string') {
    return value.split('');
  } else {
    return value
      .toString()
      .split('')
      .map((item) => parseInt(item));
  }
}
let ar = toArray(123);
console.log(ar);
let ar1 = toArray('123');
console.log(ar1);

類(本身就可以充當類型)

它可以描述實例(類類型)

// 類
class Circle {
  // 類的屬性 ts中所有的屬性都要先聲明再使用
  PI = 3.14;
  // 類的構造函數
  constructor(public radius: number) {
    this.radius = radius;
  }
  // 類的方法
  area() {
    return this.PI * this.radius ** 2;
  }
}
// 還有一種寫法
class Animals {
  constructor(public name: string) {
    // this.name = name; // 賦值也可以省去
  }
  eat() {
    console.log(this.name + ' is eating');
  }
}
const a1 = new Animals('dog');
a1.eat();
// 訪問修飾
// public 公共的 默認的
// private 私有的 只能在類的內部訪問
// protected 受保護的 只能在類的內部和子類中訪問
// readonly 只讀的 初始化之後不能修改
// static 靜態的
// abstract 抽象的

// #號開頭的屬性是私有屬性 只能在類的內部訪問 不能在類的外部訪問(這個是一個js語法)
// 不能new
class Singleton {
  private static instance: Singleton;
  private constructor() {}
  static getInstance() {
    if (!this.instance) {
      this.instance = new Singleton();
    }
    return this.instance;
  }
}
let sg1 = Singleton.getInstance();
// 抽象類不能new
// 不能實例化,只能被繼承
// 抽象類中可以擁有具體賓實現
abstract class Animal {
  constructor(public name: string) {}
  abstract eat(): void; // 原型上的方法 默認採用這種方法更好些
  abstract play:()=>void; // 實例上的方法 ts中不做區分,但是一般的是實例方法
}

接口

接口可以理解為對行為的抽象(沒有具體的實現) ,可以用於描述 對象,函數,類,混合類型

// 接口是對行為的抽象,而抽象類是對類的抽象
// 用接口描述函數
// interface 描述的是形狀或結構
interface IFullname {
  firstname: string;
  lastname: string;
}
type IFn = (obj: IFullname) => string;
let fn: IFn = ({ firstname, lastname }: IFullname): string => {
  return firstname + lastname;
};
fn({ firstname: 'John', lastname: 'Doe' });
// 1. 如果只是用來描述結構我們採用interface
// 2. 如果涉及到聯合類型,則只能使用type
// 3. type不能擴展,interface可以擴展
// 4. type不能重名,interface可以重名
// 5. type可以使用typeof獲取實例的類型,interface不行
// 6. type可以使用keyof獲取key的類型,interface不行
// 7. type可以使用infer獲取函數返回值的類型,interface不行
// 8. type可以使用extends實現交叉類型,interface不行
// 9. type可以使用條件類型,interface不行
// 10. type可以使用映射類型,interface不行
// 11. type可以使用索引訪問操作符,interface不行

// 可以用接口描述混合類型
interface IFn1{
  (a:number,b:number):number;
  prop1:string;
  prop2:number;
}
// let fn11:IFn1 = (a,b)=>a+b; // 這樣就報錯了,因為fn11沒有prop1和prop2屬性且let定義可以重新被賦值
const fn11:IFn1 = (a,b)=>a+b;
fn11.prop1 = 'Hello';
fn11.prop2 = 123;
// 一般情況下我們使用interface來描述對象,如果需要使用聯合類型,交叉類型,元組,只能使用type
interface IVeg {
  readonly color: string;
  size: number;
  taste?: 'sweet' | 'sour' | 'bitter'; // 可選屬性
}
let veg: IVeg = {
  color: 'red',
  size: 20,
};
// veg.color = 'green'; // 會報錯

如果對象中的屬性,多於接口定義的,

  1. 可以採用斷言來賦值
  2. 可以基於接口的特性寫一個同名的接口
  3. 產生新類型,通過繼承原有屬性的方式
  4. 類型兼容
  5. [key:string]:any 任意類型擴展
interface ICar {
  color: string;
  a: 1;
  b: 2;
}
type ValueOf = ICar[keyof ICar]; // 1 | 2 | string

主題測試文章,只做測試使用。發佈者:Walker,轉轉請注明出處:https://walker-learn.xyz/archives/4409

(0)
Walker的頭像Walker
上一篇 2025年3月8日 02:11
下一篇 2025年3月27日 15:01

相關推薦

  • 深入理解ES6 003【學習筆記】

    函數 參數默認值,以及一些關於arguments對象,如何使用表達式作為參數、參數的臨時死區。 以前設置默認值總是利用在含有邏輯或操作符的表達式中,前一個值是false時,總是返回後面一個的值,但如果我們給參數傳0時,就有些麻煩。需要去驗證一下類型 function makeRequest(url,timeout,callback){ timeout = t…

    個人 2025年3月8日
    1.2K00
  • 深入理解ES6 001【學習筆記】

    塊級作用域綁定 之前的變量聲明var無論在哪裡聲明的都被認為是作域頂部聲明的,由於函數是一等公民,所以順序一般是function 函數名()、var 變量。 塊級聲明 塊級聲明用於聲明在指定塊的作用域之外無法訪問的變量。塊級作用域存在於: 函數內部 塊中(字符和{和}之間的區域) 臨時死區 javascript引擎在掃描代碼發現變量聲明時,要麼將它們提升至作…

    個人 2025年3月8日
    1.6K00
  • 熱愛運動,挑戰極限,擁抱自然

    熱愛 在這個快節奏的時代,我們被工作、生活的壓力所包圍,常常忽略了身體的需求。而運動,不僅僅是一種健身方式,更是一種釋放自我、挑戰極限、與自然共舞的生活態度。無論是滑雪、攀岩、衝浪,還是跑步、騎行、瑜伽,每一種運動都能讓我們找到內心的激情,感受到生命的躍動。 運動是一場自我挑戰 挑戰極限,不僅僅是職業運動員的專屬,而是每一個熱愛運動的人都可以追求的目標。它可…

    個人 2025年2月26日
    1.3K00
  • Go工程師體系課 009【學習筆記】

    其它一些功能 個人中心 收藏 管理收貨地址(增刪改查) 留言 拷貝inventory_srv--> userop_srv 查詢替換所有的inventory Elasticsearch 深度解析文檔 1. 甚麼是Elasticsearch Elasticsearch是一個基於Apache Lucene構建的分布式、RESTful搜索和分析引擎,能夠快速地…

    個人 2025年11月25日
    23800
  • 深入理解ES6 008【學習筆記】

    迭代器(Iterator)和生成器(Generator) 這個新特性對於高效的數據處理而言是不可或缺的,你也會發現在語言的其他特性中也都有迭代器的身影:新的for-of循環、展開運算符(...)、甚至連異步編程都可以使用迭代器。 迭代器是一種特殊的對象,它具有一些專門為迭代過程設計的專有接口,所有的迭代器對象都有一個next()方法,每次調用都返回一個結果對…

    個人 2025年3月8日
    1.1K00
簡體中文 繁體中文 English
歡迎🌹 Coding never stops, keep learning! 💡💻 光臨🌹