当前位置: 首页 > news >正文

阮一峰《TypeScript 教程》学习笔记——泛型

1. 一段话总结

泛型是 TypeScript 中解决“输入与输出类型关联”的核心机制,通过类型参数(如) 建立类型逻辑,使函数、接口、类、类型别名能适配多种类型且保持类型一致性;其主要应用于函数、接口、类、类型别名四种场合,支持为类型参数设置默认值(如<T = string>)约束条件(如<T extends { length: number }>,限制类型参数需满足特定结构);数组的泛型表示为Array<T>T[]是简写),TypeScript 原生数据结构(如Map、Set、Promise)也基于泛型实现;使用时需注意尽量少用、类型参数越少越好、类型参数需出现两次及以上、支持泛型嵌套,避免过度复杂。


2. 思维导图

在这里插入图片描述


3. 详细总结

一、泛型简介

  1. 核心问题
    当函数、接口等需适配多种类型,且输入与输出类型存在关联时(如函数返回值类型与参数类型一致),直接用any会丢失类型检查,泛型通过类型参数解决此问题。
    示例:

    // 无泛型:无法关联参数与返回值类型
    function getFirst(arr:any[]):any { return arr[0]; }
    // 有泛型:明确参数(T[])与返回值(T)类型关联
    function getFirst<T>(arr:T[]):T { return arr[0]; }
    
  2. 类型参数

    • 语法:用尖括号包裹(如<T>),名称自定义(习惯用T、U、V等大写字母);
    • 调用方式:显式传入(如getFirst<number>([1,2,3]))或自动推断(如getFirst([1,2,3]),TS推断T=number);
    • 多类型参数:用逗号分隔(如<T,U>,适用于多输入类型关联,如map<T,U>(arr:T[], f:(arg:T)=>U):U[])。

二、泛型的四种写法

应用场合基础语法关键特性示例
函数泛型函数名后加类型参数支持显式/自动推断类型参数;变量形式有两种写法function id<T>(arg:T):T { return arg; }
变量写法:let myId:<T>(arg:T)=>T = id
接口泛型接口名后加类型参数(全局)/方法内加类型参数(局部)全局类型参数:接口内所有成员可用;
局部类型参数:仅对应方法可用
全局:interface Box<Type> { contents:Type }
局部:interface Fn { <Type>(arg:Type):Type }
类泛型类名后加类型参数类型参数仅用于实例成员(静态成员不可引用);
继承时需指定父类类型参数
class Pair<K,V> { key:K; value:V }
继承:class B extends A<any> {}
类型别名泛型类型别名后加类型参数支持递归定义(如树形结构);
可组合其他泛型
`type Nullable = T

三、类型参数的特性

1. 类型参数的默认值

  • 语法<T = 默认类型>,调用时若未指定类型参数且无法推断,使用默认值;
  • 规则
    • 默认值可被类型推断覆盖(如function getFirst<T = string>(arr:T[]):T,调用getFirst([1,2])时T被推断为number,覆盖默认值string);
    • 可选类型参数(有默认值)需在必选参数后(如<T, U = boolean>正确,<T = boolean, U>错误);
  • 示例
    class Generic<T = string> {list:T[] = [];add(t:T) { this.list.push(t); }
    }
    const g1 = new Generic(); // T=string,add('hello')正确,add(4)报错
    const g2 = new Generic<number>(); // T=number,add(4)正确,add('hello')报错
    

2. 类型参数的约束条件

  • 语法T extends 约束类型,限制类型参数需为“约束类型”的子类型(满足约束类型的结构);
  • 规则
    • 约束类型可为对象、接口等具体结构(如<T extends { length: number }>,要求T有length属性);
    • 多类型参数可互相引用(如<T, U extends T>,U需是T的子类型);
    • 不可自引用(如<T extends T>)或互相约束(如<T extends U, U extends T>);
    • 默认值需满足约束条件(如<A extends string, B extends string = 'world'>,B的默认值’world’符合string约束);
  • 示例
    // 约束T需有length属性
    function comp<T extends { length: number }>(a:T, b:T) {return a.length >= b.length ? a : b;
    }
    comp([1,2], [1,2,3]); // 正确(数组有length)
    comp('ab', 'abc'); // 正确(字符串有length)
    comp(1, 2); // 报错(数字无length)
    

四、数组与原生泛型结构

  1. 数组的泛型表示

    • 完整写法:Array<T>(T为数组成员类型);
    • 简写形式:T[](如number[]等同于Array<number>);
    • TypeScript 内部Array是泛型接口,定义了lengthpush()pop()等成员,确保操作与成员类型一致。
  2. 原生泛型结构
    TypeScript 原生数据结构均基于泛型实现,确保类型安全:

    结构泛型写法说明
    MapMap<K, V>K为键类型,V为值类型
    SetSet<T>T为集合成员类型
    PromisePromise<T>T为Promise resolve类型
    只读数组ReadonlyArray<T>不可修改的数组,无push()

    示例(只读数组):

    function doStuff(values:ReadonlyArray<string>) {values.push('hello'); // 报错(ReadonlyArray无push())
    }
    

五、使用注意点

  1. 尽量少用泛型:泛型会增加代码复杂性,可不用则不用(如简单类型场景直接用具体类型);
  2. 类型参数越少越好:多一个类型参数增加一次替换步骤,简化类型逻辑(如可将多余类型参数合并到函数参数类型中);
    示例优化:
    // 优化前(2个类型参数,Fn多余)
    function filter<T, Fn extends (arg:T)=>boolean>(arr:T[], func:Fn):T[] { return arr.filter(func); }
    // 优化后(1个类型参数,直接写函数类型)
    function filter<T>(arr:T[], func:(arg:T)=>boolean):T[] { return arr.filter(func); }
    
  3. 类型参数需出现两次及以上:若类型参数仅出现一次,可简化为具体类型(如<Str extends string>(s:Str)可简化为s:string);
  4. 支持泛型嵌套:类型参数可引用其他泛型,实现复杂类型组合(如type OneOrManyOrNull<Type> = OrNull<OneOrMany<Type>>)。

4. 关键问题

问题1:泛型的核心作用是什么?它相比直接使用any有什么优势?

答案
泛型的核心作用是建立输入与输出的类型关联,使函数、接口、类等能适配多种类型且不丢失类型检查(如函数参数为T[]时,返回值必为T,确保类型一致性)。
相比any,其优势在于:

  1. 保留类型检查any会关闭类型检查,导致错误在运行时暴露(如function getFirst(arr:any[]):any,返回值可被误当作任意类型使用);而泛型通过类型参数锁定类型关系(如function getFirst<T>(arr:T[]):T,返回值类型与参数类型强关联,误用会编译报错);
  2. 增强代码可读性:泛型明确了“类型适配逻辑”,读者可通过类型参数(如)快速理解输入输出的类型关系,而any无法体现类型约束。

问题2:类型参数的“约束条件(extends)”和“默认值”可以同时使用吗?有哪些关键规则需要遵守?

答案
可以同时使用,但需满足“默认值必须符合约束条件”,关键规则如下:

  1. 默认值需符合约束:若类型参数同时有约束和默认值,默认值必须是约束类型的子类型(如<A extends string, B extends string = 'world'>,默认值’world’是string的子类型,符合约束);
  2. 可选参数顺序:有默认值的类型参数(可选)需在必选类型参数之后(如<T, U = boolean>正确,<T = boolean, U>错误);
  3. 约束不可自引用/互相约束:约束条件不能是类型参数自身(如<T extends T>),也不能多参数互相约束(如<T extends U, U extends T>),否则会导致约束无效;
    示例(合法使用):
type Fn<A extends string, B extends string = 'world'> = [A, B];
type Result1 = Fn<'hello'>; // ["hello", "world"](使用默认值,符合约束)
type Result2 = Fn<'hi', 'ts'>; // ["hi", "ts"](自定义值,符合约束)

问题3:泛型在类中的使用有什么特殊限制?为什么静态成员不能引用类型参数?

答案
泛型在类中的特殊限制是静态成员(静态属性、静态方法)不能引用类型参数,原因如下:

  1. 类型参数的作用域:类的类型参数(如class C<T>)是“实例级”的,仅用于描述实例的属性和方法(每个实例可指定不同的T,如new C<number>()new C<string>()是不同实例类型);
  2. 静态成员的作用域:静态成员是“类级”的,属于类本身而非实例,在类定义时就已确定,无法关联到实例级的类型参数(若静态成员引用T,类的所有实例会共享该静态成员,导致T的类型混乱);
    示例(错误与正确对比):
class C<T> {static data: T; // 报错(静态成员引用实例级类型参数T)instanceData: T; // 正确(实例属性引用T)constructor(data:T) { this.instanceData = data; }
}
http://www.dtcms.com/a/520346.html

相关文章:

  • 数据结构——三十、图的深度优先遍历(DFS)(王道408)
  • Linux中的DKMS机制
  • springboot基于Java的高校超市管理系统设计与实现(代码+数据库+LW)
  • Qt 文件与目录操作详解:QFile, QDir, QFileInfo, 与 QTextStream
  • 【软件设计师】数据结构
  • 每日一个网络知识点:应用层E-mail
  • 黑龙江省城乡建设厅网站免费帮朋友做网站
  • 网站优化方法页面WordPress有赞支付
  • 大模型推理服务优化:vLLM的PagedAttention与连续批处理实现
  • 迅投xtquant获取当前全部的期货主力合约
  • 郑州网站建设哪家公司好wordpress 登录慢
  • 第一台 Andriod XR 设备发布,Jetpack Compose XR 有什么不同?对原生开发有何影响?
  • LeetCode算法日记 - Day 81: 最大子数组和
  • 商城网站方案加强局网站建设报告
  • 开源OpenXR引擎:Monado XR Runtime开发配置及关键模块说明(可用于自研VRAR眼镜设备或pico、queset等量产设备)
  • 创业公司做网站建设网站需要什么设施?
  • 5 倍性能优于开源版,火山 Milvus 集成 DiskANN+RaBitQ
  • saas建站系统是怎么实现的浦江做网站
  • [Java数据结构和算法] HashMap 和 HashSet
  • 什么是跨境电商主要做什么女生seo专员很难吗为什么
  • R语言数据结构与数据处理基础内容
  • 求下列线性变换的矩阵
  • iOS 打包 IPA 全流程详解,签名配置、工具选择与跨平台上传实战指南
  • 前端代码规范:husky+ lint-staged+pre-commit
  • 房地产公司网站源码图片数量 wordpress
  • Qt-UDP
  • Ethernet/ip 转 Modbus RTU 驱动,罗克韦尔 PLC 与华为逆变器打造光伏电站智能监控典范
  • 与创新者同频!与FlagOS共赴开源之约
  • 【C++学习】函数及分文件编写
  • 网站免费推广计划新闻视频网站开发