泛型
/*
* @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的安全類型,泛型沒有指定類型時,默認是unknown
let 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>; // 類型推斷為Sky
type R6 = ConditionType<Fish>; // 類型推斷為Water
type 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 unknown
type R16 = any extends string ? true : false; // true和false的聯合類型是boolean
// 針對any類型永遠返回的是成功和失敗的聯合類型;
// 低哦打可以賦予高的類型,
// 從結構上考慮 交叉類型,可以賦予 交叉前類型
// ts默認的 大小object是一樣的 Object extends object? true:false 反過來不一樣
// 結構上比你多,就可以賦予你
// 級別上 低級別可以賦予高級別
export {};
內置類型
正常判斷類型的時候,可以通過A extends B
條件分發(分發特性是默認開啓)
- A類型是通過泛型傳入的
- A類型如果是聯合類型會進行分發的
- 泛型參數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) => Childe) {
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