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的安全类型,泛型没有指定类型时,默认是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
条件分发(分发特性是默认开启)

  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) => 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://joyjs.cn/archives/4410

(0)
Walker的头像Walker
上一篇 2025年3月27日 15:01
下一篇 2025年3月27日 15:01

相关推荐

  • 【开篇】

    我是Walker,生于八十年代初,代码与生活的旅者。全栈开发工程师,游走于前端与后端的边界,执着于技术与艺术的交汇点。 代码,是我编织梦想的语言;项目,是我刻画未来的画布。在键盘的敲击声中,我探索技术的无尽可能,让灵感在代码里永恒绽放。 深度咖啡爱好者,迷恋每一杯手冲的诗意与仪式感。在咖啡的醇香与苦涩中,寻找专注与灵感,亦如在开发的世界中追求极致与平衡。 骑…

    2025年2月6日 个人
    65100
  • 深入理解ES6 003【学习笔记】

    函数 参数默认值,以及一些关于arguments对象,如何使用表达式作为参数、参数的临时死区。 以前设置默认值总是利用在含有逻辑或操作符的表达式中,前一个值是false时,总是返回后面一个的值,但如果我们给参数传0时,就有些麻烦。需要去验证一下类型 function makeRequest(url,timeout,callback){ timeout = t…

    个人 2025年3月8日
    40900
  • 深入理解ES6 008【学习笔记】

    迭代器(Iterator)和生成器(Generator) 这个新特性对于高效的数据处理而言是不可或缺的,你也会发现在语言的其他特性中也都有迭代器的身影:新的for-of循环、展开运算符(...)、甚至连异步编程都可以使用迭代器。 迭代器是一种特殊的对象,它具有一些专门为迭代过程设计的专有接口,所有的迭代器对象都有一个next()方法,每次调用都返回一个结果对…

    个人 2025年3月8日
    35200
  • 深入理解ES6 012【学习笔记】

    代理(Proxy)和反射(Reflection)API 代理是一种可以拦截并改变底层javascript引擎操作的包装器,在新语言中通过它暴露内部运作对象,从而让开发者可以创建内建的对象。 代理陷阱 覆写的特性 默认特性 get 读取一个属性值 Reflect.get() set 写入一个属性值 Reflect.set() has in操作符 Reflect…

    个人 2025年3月8日
    39200
  • 深入理解ES6 002【学习笔记】

    字符串和正则表达式 字符串和正则表达式 Javascript字符串一直基于16位字符编码(UTF-16)进行构建。每16位的序列是一个编码单元(code unit),代表一个字符。length、charAt()等字符串属性和方法都基于这个编码单元构造的。Unicode的目标是为世界上每一个字符提供全球唯一的标识符。如果我们把字符长度限制在16位,码位数量将不…

    个人 2025年3月8日
    53000

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信
欢迎🌹 Coding never stops, keep learning! 💡💻 光临🌹