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年3月27日 15:01
下一篇 2025年3月27日 15:01

相關推薦

  • Node 深入淺出(聖思園教育) 002【學習筆記】

    node 的套件管理機制和載入機制 npm search xxxnpm view xxxnpm install xxx nodejs 檔案系統操作的 api Node.js 的 fs 模組提供同步(Sync)與基於回呼/Promise 的非同步 API,可以操作本機檔案與目錄。日常開發中常用的能力包括讀取、寫入、追加、刪除、遍歷目錄、監聽變化等。以下範例基於 C…

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

    rocketmq 快速入門 請參考我們的各種配置(podman)以瞭解安裝方式。概念介紹 RocketMQ 是阿里巴巴開源、Apache 頂級專案的分散式訊息中介軟體,核心元件: NameServer:服務發現與路由 Broker:訊息儲存、遞送、拉取 Producer:訊息生產者(傳送訊息) Consumer:訊息消費者(訂閱並消費訊息) Topic/Tag:主題/…

    個人 2025年11月25日
    16700
  • Go工程師體系課 protoc-gen-validate【學習筆記】

    protoc-gen-validate 簡介與使用指南 ✅ 什麼是 protoc-gen-validate protoc-gen-validate(簡稱 PGV)是一個 Protocol Buffers 外掛程式,用於在生成的 Go 程式碼中加入結構體欄位的驗證邏輯。 它透過在 .proto 檔案中加入 validate 規則,自動為每個欄位生成驗證程式碼,避免你手…

    個人 2025年11月25日
    1.3K00
  • 深入理解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
簡體中文 繁體中文 English
歡迎🌹 Coding never stops, keep learning! 💡💻 光臨🌹