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

相关推荐

  • 从0到1落地微前端架构 001【学习笔记】

    微前端 js 隔离css 隔离元素隔离生命周期预加载数据通信应用跳转多层嵌套 说明 使用的是 Mermaid 的 flowchart 语法,Markdown 渲染器如 Typora、VitePress、一些 Git 平台都支持。 保留了: 基座应用 main-vue3 各子应用:child-nuxt2-home、child-vue2-job、child-vu…

    2025年4月20日
    1.4K00
  • Node深入浅出(圣思园教育) 002【学习笔记】

    node 的包管理机制和加载机制 npm search xxxnpm view xxxnpm install xxx nodejs 文件系统操作的 api Node.js 的 fs 模块提供同步(Sync)与基于回调/Promise 的异步 API,可以操作本地文件与目录。日常开发中常用的能力包括读取、写入、追加、删除、遍历目录、监听变化等。以下示例基于 C…

    个人 2025年11月24日
    11500
  • Go工程师体系课 protoc-gen-validate【学习笔记】

    protoc-gen-validate 简介与使用指南 ✅ 什么是 protoc-gen-validate protoc-gen-validate(简称 PGV)是一个 Protocol Buffers 插件,用于在生成的 Go 代码中添加结构体字段的验证逻辑。 它通过在 .proto 文件中添加 validate 规则,自动为每个字段生成验证代码,避免你手…

    个人 2025年11月25日
    1.2K00
  • 【开篇】

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

    2025年2月6日 个人
    2.0K00
  • Go工程师体系课 004【学习笔记】

    需求分析 后台管理系统 商品管理 商品列表 商品分类 品牌管理 品牌分类 订单管理 订单列表 用户信息管理 用户列表 用户地址 用户留言 轮播图管理 电商系统 登录页面 首页 商品搜索 商品分类导航 轮播图展示 推荐商品展示 商品详情页 商品图片展示 商品描述 商品规格选择 加入购物车 购物车 商品列表 数量调整 删除商品 结算功能 用户中心 订单中心 我的…

    个人 2025年11月25日
    10600

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

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

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