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

结构化类型VS标称类型:TS类型系统全解析

1. 结构化类型系统与标称类型系统

1. 结构化类型系统

结构化类型系统(又称鸭子类型)通过类型的结构(属性和方法) 判断兼容性,而非类型名称。只要两个类型的结构一致,就会被视为兼容。

class Cat { eat() {} }
class Dog { eat() {} }function feedCat(cat: Cat) {}
feedCat(new Dog()); // 不报错,因结构一致
class Cat { meow() {}; eat() {} } // 新增独特方法
class Dog { eat() {} }function feedCat(cat: Cat) {}
feedCat(new Dog()); // 报错,Dog缺少meow方法
class Cat { eat() {} }
class Dog { bark() {}; eat() {} } // 比Cat多一个方法function feedCat(cat: Cat) {}
feedCat(new Dog()); // 不报错,Dog完全包含Cat的结构
class Cat { eat(): boolean { return true; } }
class Dog { eat(): number { return 599; } }function feedCat(cat: Cat) {}
feedCat(new Dog()); // 报错,eat返回值类型不同

2. 标称类型系统

标称类型系统通过类型名称判断兼容性,即使结构完全一致,名称不同也会被视为不同类型。常用于区分语义不同但基础类型相同的值(如货币单位)。

type USD = number; // 美元
type CNY = number; // 人民币const usd: USD = 100;
const cny: CNY = 200;function addCNY(a: CNY, b: CNY) { return a + b; }
addCNY(cny, usd); // 结构化类型中不报错(不合理),标称类型中会报错

3. TS模拟标称类型系统

TypeScript默认是结构化类型系统,可通过附加独特标记模拟标称类型,让结构一致的类型被视为不同。

通过交叉类型给原始类型添加“隐藏标签”,仅在类型层面生效,不影响运行时。

// 定义带标签的工具类型
type Nominal<T, Tag extends string> = T & { __tag: Tag };// 应用到货币类型
type USD = Nominal<number, "USD">;
type CNY = Nominal<number, "CNY">;const usd: USD = 100 as USD;
const cny: CNY = 200 as CNY;function addCNY(a: CNY, b: CNY) { return (a + b) as CNY; }
addCNY(cny, usd); // 报错,类型不兼容(符合预期)

通过类的私有属性作为标记,利用TypeScript对私有属性的检查机制区分类型,支持运行时逻辑。

class USD {private __tag!: void; // 私有属性作为标记constructor(public value: number) {}
}class CNY {private __tag!: void; // 同名称私有属性,但属于不同类constructor(public value: number) {}
}const usd = new USD(100);
const cny = new CNY(200);function addCNY(a: CNY, b: CNY) { return a.value + b.value; }
addCNY(cny, usd); // 报错,usd是USD类型(符合预期)

总结

  • 结构化类型系统:基于类型结构判断兼容性,广泛用于TypeScript、Python等语言。
  • 标称类型系统:基于类型名称判断兼容性,用于C++、Java等语言,可避免语义冲突(如货币单位)。
  • 模拟标称类型:在TypeScript中通过“附加标记”(类型交叉或类私有属性)实现,提升类型安全性。

2.TS类型系统层级

判断类型兼容性的方式

介绍了两种判断类型兼容性的方法,条件类型通过extends判断,变量赋值通过能否赋值判断子类型关系。

type Result = 'linbudu' extends string ? 1 : 2; // 1,说明'linbudu'是string的子类型
declare let source: string;
declare let anyType: any;
declare let neverType: never;
anyType = source; // 成立,string是any的子类型
// neverType = source; // 报错,string不是never的子类型

从原始类型开始

原始类型(如string、number)与其对应的字面量类型(如"linbudu"、1)存在父子关系,字面量类型是原始类型的子类型;对象类型相关的字面量(如{}、[])也是object的子类型。

type Result1 = "linbudu" extends string ? 1 : 2; // 1
type Result2 = 1 extends number ? 1 : 2; // 1
type Result4 = { name: string } extends object ? 1 : 2; // 1
type Result6 = [] extends object ? 1 : 2; // 1

向上探索,直到穹顶之上

字面量类型是包含它的联合类型的子类型,原始类型是包含它的联合类型的子类型;同一基础类型的字面量联合类型是该基础类型的子类型。

type Result7 = 1 extends 1 | 2 | 3 ? 1 : 2; // 1
type Result10 = string extends string | false | number ? 1 : 2; // 1
type Result11 = 'lin' | 'bu' | 'budu' extends string ? 1 : 2; // 1

原始类型(如string)是其装箱类型(如String)的子类型,装箱类型是Object的子类型;结构化类型系统中,String是{}的子类型,{}是object的子类型等,存在特殊兼容关系。

type Result14 = string extends String ? 1 : 2; // 1
type Result15 = String extends {} ? 1 : 2; // 1
type Result16 = {} extends object ? 1 : 2; // 1

any和unknown是类型层级的顶端,Object是它们的子类型;any比较特殊,any extends T会返回1|2(同时包含成立和不成立的情况),且any与unknown互相兼容。

type Result22 = Object extends any ? 1 : 2; // 1
type Result23 = Object extends unknown ? 1 : 2; // 1
type Result24 = any extends Object ? 1 : 2; // 1 | 2
type Result31 = any extends unknown ? 1 : 2; // 1

向下探索,直到万物虚无

never是类型层级的最底层,是任何类型的子类型;null、undefined、void是独立类型,不是其他原始类型的子类型(严格模式下)。

type Result33 = never extends 'linbudu' ? 1 : 2; // 1
type Result34 = undefined extends 'linbudu' ? 1 : 2; // 2
type Result35 = null extends 'linbudu' ? 1 : 2; // 2

组合上述结论可形成完整的类型层级链,从never向上依次为:字面量类型 → 包含该字面量的联合类型 → 原始类型 → 装箱类型 → Object → any/unknown,所有层级关系可通过嵌套条件类型验证。

type TypeChain = never extends 'linbudu'? 'linbudu' extends 'linbudu' | '599'? 'linbudu' | '599' extends string? string extends String? String extends Object? Object extends any? any extends unknown? unknown extends any? 8: 7: 6: 5: 4: 3: 2: 1: 0; // 结果为8,所有条件成立

其他比较场景

基类与派生类:派生类是基类的子类型(因保留基类结构并新增内容)。

联合类型子集:若联合类型A的所有成员都在联合类型B中,则A是B的子类型。

type Result36 = 1 | 2 | 3 extends 1 | 2 | 3 | 4 ? 1 : 2; // 1
type Result38 = 1 | 2 | 5 extends 1 | 2 | 3 | 4 ? 1 : 2; // 2

数组和元组:元组成员类型符合数组类型时,元组是数组的子类型;空数组是任意数组的子类型等。

type Result40 = [number, number] extends number[] ? 1 : 2; // 1
type Result43 = [] extends number[] ? 1 : 2; // 1

总结与预告

本文通过从原始类型向上、向下探索,明确了TypeScript的类型层级(从never到any/unknown),涵盖了联合类型、装箱类型等特殊情况的兼容性规则。学习类型层级是理解条件类型的基础,下一节将讲解条件类型及infer等概念。

http://www.dtcms.com/a/565632.html

相关文章:

  • Git笔记之Git下载、拉取项目、Webstorm更新Git项目报错识别不到git
  • Linux之arm SMMUv3 控制器注册过程分析(7)
  • 临沧市住房和城乡建设网站企业咨询管理公司简介
  • 13-卷积神经网络(CNN):探讨CNN在图像处理中的应用和优势
  • Spring Boot3零基础教程,StreamAPI 的基本用法,笔记99
  • seo关键词排名优化教程seo网站架构设计
  • 宿州做企业网站公司咸阳网站制作公司
  • 一个空间建多个网站的方法wordpress显示用户列表
  • Java中的数组(续)
  • 2025年内蒙古自治区职业院校技能大赛高职组 “信息安全管理与评估”竞赛样题(一)
  • 嵌入式Linux电源管理实战 --深入解析CPU调频governor原理与优化
  • PostIn零基础学习 - 如何快速设计并分享接口文档
  • 我想建立一个网站不知道怎么做啊小白怎么做网站
  • OpenLCA生命周期评估模型构建与分析
  • AR眼镜赋能船舶巡检:打造智能化运维新方案
  • 从“被动监控”到“主动预警”:EasyGBS远程视频监控方案助力企业高效安全运营
  • 《A Bilateral CFAR Algorithm for Ship Detection in SAR Images》译读笔记
  • 网站图标 psd门户网站的优点
  • 中国交通建设集团网站单页主题 wordpress
  • 网站建设 年终总结沈阳市建设工程安全监督站网站
  • 2.1.2.CSS3
  • 线性代数 - 线性方程组的 LU 分解解法
  • 学习中小牢骚1
  • 游戏网站怎么做seo网站怎么做下载网页代码吗
  • 太原网站设计制作网站开发网站说明怎么写
  • 告别乱码:OpenCV 中文路径(Unicode)读写的解决方案
  • 41_AI智能体核心业务之意图识别Agent:智能对话系统的决策大脑
  • 大数据毕业设计项目推荐 基于大数据的广西药店数据可视化分析系统 1.65w条数据【大数据毕业设计项目选题】
  • 猎豹算法详细原理,公式,应用案例—基于猎豹算法的函数优化
  • 实时通讯的稳定性秘诀:cpolar优化Websocket连接