TS珠峰 002【學習筆記】

泛型

/* * @Author: error: error: git config user.name & please set dead value or install git && error: git config user.email & please set dead value or install git & please set dead value or install git * @Date: 2025-03-19 10:42:31 * @LastEditors: error: error: git config user.name & please set dead value or install git && error: git config user.email & please set dead value or install git & please set dead value or install git * @LastEditTime: 2025-03-19 15:11:32 * @FilePath: /ts-classes/src/index.ts * @Description: 這是默認設置,請設置`customMade`, 打開koroFileHeader查看配置 進行設置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE */// 泛型class Animal {  constructor(public name: string, public age: number) {}}class Person {  constructor(public name: string, public age1: number) {}}// ts中在使用的時候,可以通過function createInstance<T>(c: { new (...args: any[]): T }, ...args: any[]): T {  return new c(...args);}// const animal = new Animal('dog', 12);// console.log(animal.name);// console.log(animal.age);const animal = createInstance<Animal>(Animal, 'dog', 12);const person = createInstance(Person, 'Tom', 12);// 類型不確定,我們可以根據泛型來確定類型// 一定要注意ts是沒有執行呢,只是在編譯的時候會進行類型檢查// 根據提供的數據生成對應長度的數組function createArray<T>(length: number, value: T): T[] {  return Array.from({ length }, () => value);}// 交換兩個變量的值type ISwap = <T, U>(tuple: [T, U]) => [U, T];interface ISwap2 {  <T, U>(tuple: [T, U]): [U, T]; // 為甚麼<T, U>要寫在這裡,而不是在函數後面? 泛型使用的時候傳遞類型,可以推導,但是內部調用的時候沒有確定類型}// 寫在定義的前端,就表示使用類型的時候傳參,寫到函數的前端意味著函數調用的時候傳參// 是在使用類型的時候傳遞泛型,還是在調用函數的時候傳遞泛型const swap: ISwap = (tuple) => {  return [tuple[1], tuple[0]];};const swapArray = swap<number, string>([1, '2']);// 泛型是有默認值的,使用一些聯合類型的時候,會使用泛型type UnionType<T = boolean> = T | number | string;let union: UnionType = '123';// 泛型約束 要求傳遞的參數必須符合要求 A extends B 表示A是B的子類(或同類型)interface ILength {  length: number;}// 甚麼叫子 甚麼叫父function getLength<T extends ILength>(value: T) {  // 只要我的對象里有length屬性,就可以  return value.length;}getLength('123');getLength({ length: 123 });function getValue<T, K extends keyof T>(obj: T, key: K) {  return obj[key];}getValue({ name: '張三', age: 12 }, 'age');interface ILoginResponse<T> {  code: number;  message?: string;  data: T;}interface ILoginData {  token: string;  userInfo: {    name: string;    age: number;  };  roles: string[];}function toLogin<T>(response: ILoginResponse<T>) {  return response.data;}const loginResponse = toLogin<ILoginData>({  code: 200,  data: {    token: '123',    userInfo: {      name: '張三',      age: 12,    },    roles: ['admin', 'user'],  },});// 類中使用泛型class Cache<T> {  private data: T[] = [];  add(item: T) {    this.data.push(item);  }}const cache = new Cache<string>();cache.add('123');cache.add('456');export {};

交叉型別

// & 交叉型別 | 聯集型別// 將多個型別合併成一個型別interface Person1 {  handsome: string;}interface Person2 {  high: string;}// 又高又帥的人 (交叉型別)type Person = Person1 & Person2;const person: Person = {  // handsome: '帥',  high: '高',};// 交叉型別可以賦給任意的父型別export {};

unkown

// unknown 型別// unknown 是 any 的安全型別,泛型沒有指定型別時,預設是 unknownlet a: unknown;// 預設情況下 unknown 必須要進行型別檢測才能使用(型別檢查或斷言)// 型別斷言let b = a as string;// unknown 不能直接呼叫方法 需要加斷言function processInput(val: unknown) {  if (typeof val === 'string') {    console.log(val.toUpperCase());  } else if (typeof val === 'number') {    console.log(val.toFixed(2));  } else {    console.log('unknown');  }}let name: unknown = 'String字符串';// name.toUpperCase(); // name 是 unknown 型別,不能直接呼叫方法(name as string).toUpperCase();// unknown 在聯集或交叉型別中的特點type UnionType = unknown | string; // unknown 型別type IntersectionType = unknown & string; // string 型別let union: UnionType = 'String字符串';let intersection: IntersectionType = 'String字符串';export {};

條件型別

// 條件型別// 條件型別是 TS 中的一種型別運算子,它根據條件表達式的結果來選擇不同的型別type ResStatusMessage<T extends number> = T extends 200 | 204 | 206  ? 'success'  : 'error';// type R1 = ResStatusMessage<'abc'>; // 狀態碼肯定是數字,這樣傳也不會報錯type R2 = ResStatusMessage<200>; // 狀態碼肯定是數字,這樣傳也不會報錯type Condition<T, U> = T extends U ? 'success' : 'fail';type R3 = Condition<'abc', string>; // 型別推斷為 'success'type R4 = Condition<'abc', number>; // 型別推斷為 'fail'// 條件型別在函式中的應用interface Bird {  fly: () => void;}interface Sky {  name: '天空';}interface Fish {  swim: () => void;}interface Water {  name: '水';}type ConditionType<T> = T extends Bird ? Sky : Water;type R5 = ConditionType<Bird>; // 型別推斷為 Skytype R6 = ConditionType<Fish>; // 型別推斷為 Watertype FromatReturnType<T extends string | number> = T extends number  ? number  : T extends string  ? string  : never;// 泛型一般代表輸入是不是確定(無限的)約束, 函式重載(有限的)function sum<T extends number | string>(a: T, b: T): FromatReturnType<T> {  // function sum<T extends number | string>(a: T, b: T):T {  // 如果回傳值是 number 型別,則回傳 number 型別,如果回傳值是 string 型別,則回傳 string 型別,但這裡不能寫回傳值 T  return a + (b as any); // T+T 不能確定,兩個泛型不能做資料運算}let r1 = sum(1, 2);let r2 = sum('1', '2');// 我們知道條件運算子,就可以掌握 TS 中的相容性,以及型別的層級// 型別層級// 1. 基礎型別// 2. 複合型別// 3. 函式型別// 4. 類別型別// 相容性:可以將一個值賦予給某個值// 型別層級 低的層級可以賦予給高的層級

型別層級相容

// 型別層級// 1. 基礎型別// 2. 複合型別// 3. 函式型別// 4. 類別型別// 我們知道了條件運算子就可以掌握 TS 中的相容性以及型別的層級// 相容性:就是可以將一個值賦予給某個值// 型別層級:低的層級可以賦予給高的層級// 理論,誰是父型別,誰是子型別type R1 = 'abc' extends string ? true : false;type R2 = 123 extends number ? true : false;type R3 = true extends boolean ? true : false;// 實戰 'abc' 是字串型別, 123 是數字型別, true 是布林型別let r1: string = 'abc';let r2: number = 123;type R4 = 'a' extends 'a' | 'b' | 'c' ? true : false;type R5 = 1 extends 1 | 2 | 3 ? true : false;type R6 = true extends true | false ? true : false;//// 字面量型別可以賦值字面量聯集型別let r4: 'a' | 'b' | 'c' = 'a';// 包裝型別可以賦值給基礎型別// let r5: string = new String('abc');// 包裝型別type R7 = string extends String ? true : false;type R8 = String extends string ? true : false;type R9 = number extends Number ? true : false;type R10 = Number extends number ? true : false;type R11 = boolean extends Boolean ? true : false;type R12 = Boolean extends boolean ? true : false;type R13 = Object extends any ? true : false;type R14 = Object extends unknown ? true : false;type R15 = never extends 'abc' ? true : false;// never 是最小的型別// 字面量型別可以賦予給字面量聯集型別// 字面量型別可以賦予給基礎型別// 基礎型別是包裝型別的子型別// any unknown 是最大的型別// never<字面量型別<字面量聯集型別<基礎型別<包裝型別<Object<any unknowntype R16 = any extends string ? true : false; // true 和 false 的聯集型別是 boolean// 針對 any 型別永遠回傳的是成功和失敗的聯集型別;// 低階可以賦予高階型別,// 從結構上考慮 交叉型別,可以賦予 交叉前型別// TS 預設的 大小 object 是一樣的 Object extends object? true:false 反過來不一樣// 結構上比你多,就可以賦予你// 級別上 低級別可以賦予高級別export {};

內建型別

正常判斷型別的時候,可以透過 A extends B
條件分發(分發特性是預設開啟)

  1. A 型別是透過泛型傳入的
  2. A 型別如果是聯集型別會進行分發的
  3. 泛型參數 A, 必須是完全裸的(不是 A & {}),才具備分發型別
// 條件型別在函式中的應用
interface Bird {
  fly: () => void;
}
interface Sky {
  name: '天空';
}
interface Fish {
  swim: () => void;
}
interface Water {
  name: '水';
}

type Condition = Fish | Bird extends Fish ? 'success' : 'fail'; // 型別推斷為 'fail' 沒有分發
type Condition2<T> = T extends Fish ? 'success' : 'fail'; // 型別推斷為 'success' 分發

type C3 = Condition2<Fish>; // success
// 分發就是挨個去比較
// Condition2<Bird> fail
// Condition2<Fish> success
type C4 = Condition2<Bird | Fish>; // success | fail
// 預設情況下有些時候我們需要關閉這種分發能力,會造成判斷不準確
// 怎麼消除這個分發型別 T & {}
type NoDistribute<T> = T & {};
// 不是裸型別,需要 &{} 就喪失分發的能力,還可以加陣列
type Condition5<T, U> = [T] extends [U] ? true : false;
type R5 = Condition5<1 | 2, 1>; // false

// T & {} 可以消除分發能力 什麼也沒發生,只是為了 T 產生一個新型別
type IsNever<T> = T extends never ? true : false;
type R6 = IsNever<never>; // never 直接回傳 never 陣列包裹一下就是 true 了

//  內建型別
// 1. Extract 提取
type Extract<T, U> = T extends U ? T : never;
type R7 = Extract<1 | 2, 1>; // 1 分發 判斷
// 2. Exclude 排除
type Exclude<T, U> = T extends U ? never : T;
type R8 = Exclude<1 | 2, 1>; // 2 分發 判斷

// 3. NonNullable 非空
type NonNullable<T> = T extends null | undefined ? never : T;
type R9 = NonNullable<string | null>; // string

// 4. ReturnType 回傳型別 infer 推斷 可以在條件型別中推斷出回傳型別的某一部分
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : never;
type R10 = ReturnType<() => string>; // string

// 5. Parameters 參數型別
type Parameters<T> = T extends (...args: infer P) => any ? P : never;
type R11 = Parameters<(name: string, age: number) => string>; // [string, number]

// 6. ConstructorParameters 建構函式參數型別
type ConstructorParameters<T> = T extends new (...args: infer P) => any
  ? P
  : never;
type R12 = ConstructorParameters<Error>; // [message?: string | undefined]

// 7. Partial 可選
type Partial<T> = {
  [P in keyof T]?: T[P];
};
type R13 = Partial<{ name: string; age: number }>; // { name?: string; age?: number }


// 條件型別在陣列中的應用
type Swap<T> = T extends [infer A, infer B] ? [B, A] : T;
type R1 = Swap<['jw', 30]>; // 30 'jw'

// 條件型別在函式中的應用
type IsEqual<A, B> = A extends B ? (B extends A ? true : false) : false;
type R2 = IsEqual<1, 2>; // false
type R3 = IsEqual<1, 1>; // true

export {};
type Swap<T> = T extends [infer A, infer B] ? [B, A] : T;
type R1 = Swap<['jw', 30]>; // 30 'jw'
// 頭尾交換
type SwapHeadTail<T> = T extends [infer A, ...infer B, infer C]
  ? [C, ...B, A]
  : T;
type R2 = SwapHeadTail<[1, 2, 3, 4, 5]>; // [5, 2, 3, 4, 1]

// 條件型別在字串中的應用
type IsString<T> = T extends string ? true : false;
type R14 = IsString<'jw'>; // true

// 條件型別在函式重載中的應用
type IsFunction<T> = T extends (...args: any[]) => any ? true : false;
type R15 = IsFunction<() => void>; // true

// Promise 的遞迴
type PromiseReturnType<T> = T extends Promise<infer P>
  ? PromiseReturnType<P>
  : T;

function getVal(): Promise<number> {
  return new Promise((resolve) => {
    resolve(100);
  });
}
type R16 = PromiseReturnType<ReturnType<typeof getVal>>; // true
// 透過 infer 來實現遞迴推導
// 將元組轉化成聯集型別 [number,boolean,string] => number | boolean | string
type TupleToUnion<T> = T extends [infer A, ...infer B]
  ? A | TupleToUnion<B>
  : T;
type R17 = TupleToUnion<[number, boolean, string]>; // number | boolean | string

// 將聯集型別轉化成元組型別
type UnionToTuple<T> = T extends infer P ? [P] : never;
type R18 = UnionToTuple<number | boolean | string>; // [number] | [boolean] | [string]

// 重構型別結構 Partial Required Readonly Pick Omit Record

interface Person {
  name: string;
  age: number;
  gender: string;
}
let person: Partial<Person> = {
  name: 'jw',
};

function mixin<T, U>(a: T, b: U): T & U {
  return { ...a, ...b };
}
let p = mixin({ name: 'jw' }, { age: 100 });
let x = mixin(
  { name: 'jiangwen', age: 30, c: 3 },
  { name: 123, age: 30, b: 2 }
);
// 出現交叉型別了,沒有按目標合併,比如保留後面的內容
// 針對這種情況,應用將 B 有的屬性在 A 中的移除
function mixin2<T, U>(a: T, b: U): Omit<T, keyof U> & U {
  return { ...a, ...b };
}
let y = mixin2(
  { name: 'jiangwen', age: 30, c: 3 },
  { name: 123, age: 30, b: 2 }
);

type nameType = (typeof y)['name'];

// 保留想要的 key-->value 的結構 一些映射型別
type Record<K extends keyof any, T> = {
  [P in K]: T;
};
type R19 = Record<'a' | 'b', string>; // { a: string; b: string }

export {};

相容性

// 鴨子型別檢測,結構化型別檢測
// 子型別可以賦予給父型別,從結構角度出發 TS 比較的不是型別的名稱,而是這個結構上的屬性和方法
// 基礎型別的相容性
let obj = {
  toString: () => '123',
};
let str: string = '234';

obj = str; // 從安全的角度,你要的屬性我都滿足 有其他屬性,我就不相容了
// 2. 介面相容性
interface Person {
  name: string;
  age: number;
}
interface Animal {
  name: string;
  age: number;
  address: string;
}
let p: Person = { name: 'jw', age: 100 };
let a: Animal = { name: 'jw', age: 100, address: 'beijing' };

p = a; // 從安全的角度,你要的屬性我都滿足 有其他屬性,我就不相容了
// 3. 函式相容性
type Func = (a: number, b: number) => void;
let f1: Func = (a, b) => {};
let f2: Func = (a, b) => {}; //參數只能少,不能多 (比如我們使用forEach

f1 = f2; // 從安全的角度,你要的參數我都滿足 有其他參數,我就不相容了

// 4. 類別相容性
class A {
  constructor(public name: string) {}
}
class B {
  constructor(public name: string, public age: number) {}
}
let a1 = new A('jw');
let b1 = new B('jw', 100);

a1 = b1; // 從安全的角度,你要的屬性我都滿足 有其他屬性,我就不相容了

// 函式的逆變與協變 函式的參數是逆變,回傳值是協變
class Parent {
  house() {}
}
class Child extends Parent {
  car() {
    console.log('child car');
  }
}
class GrandSon extends Child {
  money() {
    console.log('grandson money');
  }
}

function fn(callback: (instance: Child) => Child) {
  let child = new Child();
  let ins = callback(child);
  return ins;
}
// 為什麼賦予的函式,可以寫 Parent 型別,但不能寫 GrandSon 型別,內部呼叫的時候傳遞的是 Child 型別,在拿到這個實例時不能訪問 Child 訪問不到的屬性
fn((instance: Child) => {
  return new Child();
});

// 回傳值應該回傳值型別的子型別(分別在不同的角度看)
fn((instance: Child) => {
  return new GrandSon();
});
// 對於函式的相容性而言,參數個數要少,傳遞可以是父類,回傳值可以回傳子類
// 推導公式
type Arg<T> = (arg: T) => void;
type Return<T> = (arg: any) => T;
type ArgType = Arg<Parent> extends Arg<Child> ? true : false; // 逆變
type ReturnType = Return<GrandSon> extends Return<Child> ? true : false; // 協變

// 所以函式的參數是逆變,回傳值是協變,

// 列舉是不具備相容性的
// TS 比較型別結構時,比較的是屬性和方法,如果屬性和方法都滿足,那麼就具備相容性

// 透過交叉型別,實現標稱型別
type Normal<T, K extends string> = T & { __type__: K };
type BTC = Normal<number, 'BTC'>;
type ETH = Normal<number, 'ETH'>;
type USDT = Normal<number, 'USDT'>;

let btc: BTC = 100 as BTC;
let eth: ETH = 200 as ETH;
let usdt: USDT = 300 as USDT;

function getVal(val: BTC) {
  return val.valueOf();
}

getVal(btc); // 傳其他就報錯了

export {};

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

(0)
Walker的頭像Walker
上一篇 2025年11月25日 05:00
下一篇 2025年11月25日 03:00

相關推薦

  • TS珠峰 003【學習筆記】

    裝飾器 // 裝飾器 // 只能在類別中使用(類別本身,類別成員使用) // 裝飾器,類別的裝飾器,屬性裝飾器,存取器裝飾器 參數裝飾器 // 1. 類別裝飾器 給類別進行擴充,也可以回傳一個子類別 // 必須先在tsconfig.json中啟用experimentalDecorators const classDecorator1 =

    個人 2025年3月27日
    1.5K00
  • Go 工程師體系課 004【學習筆記】

    需求分析 後台管理系統 商品管理 商品列表 商品分類 品牌管理 品牌分類 訂單管理 訂單列表 使用者資訊管理 使用者列表 使用者地址 使用者留言 輪播圖管理 電商系統 登入頁面 首頁 商品搜尋 商品分類導覽 輪播圖展示 推薦商品展示 商品詳情頁 商品圖片展示 商品描述 商品規格選擇 加入購物車 購物車 商品列表 數量調整 刪除商品 結帳功能 使用者中心 訂單中心 我的…

    2025年11月25日
    31200
  • 深入理解ES6 001【學習筆記】

    區塊作用域繫結 之前的變數宣告 `var` 無論在哪裡宣告,都會被視為作用域頂部宣告。由於函式是一等公民,因此順序通常是 `function 函式名稱()`、`var 變數`。 區塊宣告 區塊宣告用於宣告在指定區塊的作用域之外無法存取的變數。區塊作用域存在於: 函式內部 區塊中(字元 `{` 和 `}` 之間的區域) 暫時性死區 JavaScript 引擎在掃描程式碼發現變數宣告時,要麼會將它們提升至作…

    個人 2025年3月8日
    1.9K00
  • Node深入淺出(聖思園教育) 001【學習筆記】

    node 從非同步程式設計典範理解 Node.js Node.js 的定位與核心思想 基於 V8 引擎 + libuv 事件驅動函式庫,將 JavaScript 從瀏覽器帶到伺服器端。 採用單執行緒事件迴圈處理 I/O,最大化利用 CPU 等待 I/O 的時間切片,特別適合高併發、I/O 密集型情境。 「不要阻塞主執行緒」是設計哲學:盡量把耗時操作交給核心或執行緒池,回呼結果…

    個人 2025年11月24日
    40700
  • Go工程師體系課 017【學習筆記】

    限流、熔斷與降級入門(含 Sentinel 實戰) 結合課件第 3 章(3-1 ~ 3-9)的影片要點,整理一套面向初學者的服務保護指南,幫助理解「為什麼需要限流、熔斷和降級」,以及如何用 Sentinel 快速上手。 學習路線速覽 3-1 理解服務雪崩與限流、熔斷、降級的背景 3-2 Sentinel 與 Hystrix 對比,明確技術選型 3-3 Sen…

    個人 2025年11月25日
    28300
簡體中文 繁體中文 English