news 2026/7/2 12:04:49

别再被 TS 类型冲突折磨了!一文搞懂类型合并规则

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
别再被 TS 类型冲突折磨了!一文搞懂类型合并规则

之前学习了TypeScript的类型定义,我们都知道开发语言中的变量会有覆盖声明的情况,那么对于类型定义是不是也会有这种情况,那么该如何正确利用这种合并规则?在遇到多个类型定义的时候,我们又该如何处理?
了解类型合并规则,有助于我们定义类型,避免类型冲突。可以利用这种合并规则,更灵活的定义类型。
不同版本TypeScript,不同配置可能会导致合并差异,这里说明使用的版本为"typescript": "^5.9.3",开启了配置"strict": true,测试文件后缀为.ts.d.ts文件可能更为宽松。
同名同类型合并
TypeScript仅支持接口interface、命名空间namespace、函数声明function同名合并。在声明解析阶段即完成合并。早于其他类型引用处理,比如交叉/联合类型。
对于typeclassenum等定义的同名类型都不能合并。
interface接口声明合并
同一作用域下的同名接口会自动完成合并,无需额外语法。
合并特性:

  • 属性、方法、索引签名均可合并。
  • 同名属性、方法必须类型兼容,否则编译器报错。
  • 同一作用域/模块下。
interface Animal { name: string; } interface Animal { age: number; } // 实例必须包含name和age属性 const dog: Animal = { name: "", age: 0, };

同属性,不同类型不兼容,编译器报错。

interface Animal { name: string; } // ❌ 后续属性声明必须属于同一类型。属性“name”的类型必须为“string”,但此处却为类型“number”。 interface Animal { name: number; }

同属性同类型,不同修饰符不兼容,编译器报错。

interface Animal { name: string; } // ❌ 不同修饰符不兼容,编译器报错 interface Animal { name?: string; }

namespace命名空间合并
同名的命名空间会自动合并内部导出的成员。仅export导出的成员会合并。

namespace Utils { export function getName() { return "hboot"; } } namespace Utils { export function getAge() { return 18; } } Utils.getName(); Utils.getAge();

不允许同名成员导出。

namespace Utils { export function getName() { return "hboot"; } } namespace Utils { // ❌ 成员已存在,不允许重复定义 export const getName = () => { return 18; }; } function 函数声明合并 函数同名合并,我们称之为函数重载。TS编译器会按照倒序匹配,也就是后声明函数重载优先级高。 函数重载最后一个函数声明必须实现内部逻辑,并且参数数量、参数类型和返回值类型必须兼容。 仅使用function声明的函数支持 function getName(name: string): string; function getName(age: number): number; function getName(nameOrAge: string | number): string | number { return nameOrAge; } getName("hboot"); getName(18);

interface接口中定义的方法也会形成重载。但是和普通的函数重载最后的函数实现参数、返回值类型定义有些不同。

interface Animal { getVal(name: string): string; } interface Animal { getVal(age: number): number; } // ❌ 这样写编译器会直接报错,提示不能将类型 "string | number" 无法分配给类型 "string"。 const dog: Animal = { getVal(value: string | number): string | number { return value; }, };

普通函数重载是满足其一即可;而接口中方法重载是必须精准匹配类型每一个类型。利用TS类型推导通过泛型参数锁定类型。

// 利用了类型的自动推导,通过泛型参数 T 锁定输入类型及返回类型 const dog: Animal = { getVal<T extends string | number>(value: T): T { return value; } };

同名不同类型合并
同名不同类型的合并,主要是命名空间namespace + interface/class/function的合并,namespace可以提供静态属性、方法。
namespace+interface合并
同名的namespace命名空间为interface接口扩展静态成员;接口提供类型约束。

interface Animal { name: string; } namespace Animal { export const age = 18; export function getName() { return "hboot"; } } const dog: Animal = { name: "hboot", }; Animal.getName();

同名的namespace命名空间为class类扩展静态成员;类声明必须在命名空间的声明必之前,命名空间不能声明类已有的成员。

class Animal { name: string; static age: number; constructor(name: string) { this.name = name; } } namespace Animal { export const name = "hboot"; // ❌ 此处扩展静态成员 age 报错,类中已存在 age 静态成员 export const age = 18; export function getName() { return "hboot"; } } const dog: Animal = new Animal("admin"); Animal.getName(); // hboot Animal.name; // admin dog.name;

namespace+function合并
同名的namespace命名空间为function函数扩展静态成员。函数保持自身的可调用能力。

function speak(name: string) { return "Hello World! " + name; } namespace speak { // ❌ 此处无法覆盖 函数的 name 属性;name 是只读属性 // ❌ 类型校验没有报错,但运行时因为只读而报错 export const name = "hboot"; export function getName() { return "hboot"; } } speak("hboot"); speak.name; speak.getName();

扩展的静态成员最好不要覆盖函数本身的属性,比如namelength等。这些只读属性无法被覆盖,在运行时会报错。
interface+class合并
同名的interface接口为class类扩展实例成员。类继承接口的属性、方法,实例必须同时满足接口和类的约束。
合并特性:

  • 接口的必选属性,在类中必须显式实现,否则执行报错。
  • 同名属性,必须类型兼容,否则执行报错。
interface Animal { name: string; getName(): string; } class Animal { age: number; constructor(age: number, name: string) { this.age = age; this.name = name; } // 必须显示实现 接口 的方法成员 getName() { return this.name; } } const dog: Animal = new Animal(18, "hboot"); dog.age; // 类中需通过构造函数 dog.name; // ❌ 如果类没有显示实现;智能提示存在方法,实际调用会报错。 dog.getName();

除了手动赋值扩展属性外,可以通过public修饰符自动生成

class Animal { constructor(age: number, public name: string) { this.age = age; // 无需手动赋值 // this.name = name; } const dog: Animal = new Animal(18, "hboot"); // ... }

显示类型合并
上述的同名同类型、同名不同类型合并实际最终也是属性的合并。对于非同名属性合并则是扩展;同名属性则有一些合并规则。
对于不同类型之间的同名属性合并都有自己的规则,比如:interface+class合并要求属性类型兼容;namespace+class合并要求命名空间不能包含类已有的成员。
通过手动将一些类型合并到一个类型中,例如交叉类型&和联合类型|
交叉类型&关系合并
交叉类型将多个类型合并为一个新类型。新类型必须满足所有类型约束。
合并特性:

  • 不同名属性合并为属性并集。保留属性修饰符。
  • 同名属性取兼容类型,对于修饰符?,存在必选时则属性必选;修饰符readonly,存在属性可修改时则属性可修改。
// 不兼容类型 never type A = string & number; // 不同名属性并集 type B = { name: string } & { age: number }; // 可选 ? 修饰符, 兼容类型 name 为必选属性 type C = { name?: string } & { name: string }; // 只读 readonly 修饰符, 兼容类型 age 为可修改 type D = { readonly age: number } & { age: number };

联合类型|关系合并
将多个类型组合为一个新类型。新类型只需要满足其中一个类型约束。
合并特性:

  • 仅能访问公共属性。需通过类型守卫收窄类型后才能访问非公共属性。
  • 完全一致的类型自动去重。
type A = { name: string; age: number; }; type B = { name: string; address: string; }; type C = A | B; // 满足其中一个类型约束 const c: C = { name: "hboot", age: 18, }; // 访问非公共属性 function viewC(data: C) { if ("age" in data) { return data.age; } return data.address; }

.d.ts中的声明合并
.d.ts文件和.ts文件的合并核心规则一致。在执行时机、作用域、编译行为上有些不一样。

  • .d.ts文件中仅有类型声明,无实际代码实现,仅用于TS类型检验。所以不同于.ts文件。它会在类型校验阶段早期执行,优先合并全局/模块类型;而.ts文件是在编译阶段执行。
  • .d.ts类型优先级低,能被.ts文件显示类型声明覆盖。
  • .d.ts跨文件同名声明合并(无import/export)。.ts仅在同一个文件中同名声明合并。

declare
declare主要作用存在性声明,告诉TypeScript编译器无需生成对应的代码。

  • 全局变量/函数声明,比如:外部加载的js文件,挂载到window上的变量。
  • 扩展已有类型(与同名接口/命名空间合并)
  • 声明模块(非TS模块,比如.css.png等静态资源)

declare扩展已有类型合并规则与.ts文件的合并核心规则一致.

interface Animal { name: string; getName(): string; } declare interface Animal { age: number; }

.ts模块中没有import/export时,通过declare声明为全局作用域。如果存在import/export则需要通过declare global扩展全局作用域。

原文: https://juejin.cn/post/75924446
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/29 23:24:10

全网最全robotframework自动化测试环境搭建

一、前言 1、在2019年之前&#xff0c;robotframework-ride的版本一直是1.5.2.1&#xff0c;是2016年1月份的版本&#xff0c;只能安装在python2.7的环境上&#xff0c;导致如果想同时使用robotframework做测试且又需要python3环境编写python代码的小伙伴都需要在操作系统上安…

作者头像 李华
网站建设 2026/6/30 3:01:32

Spring Cloud核心架构组件深度解析(原理+实战+面试高频)

引言&#xff1a;在微服务架构盛行的当下&#xff0c;Spring Cloud作为基于Spring Boot的微服务开发一站式解决方案&#xff0c;凭借其完整的组件生态、灵活的配置机制和成熟的实践方案&#xff0c;成为了Java后端微服务开发的主流框架。它通过一系列核心组件解决了微服务架构中…

作者头像 李华
网站建设 2026/6/30 21:41:24

单元测试框架 Playwright 使用入门

playwright 介绍 Playwright 是一个端到端&#xff08;E2E&#xff09;测试框架&#xff0c; 它可在所有现代浏览器中运行功能强大的测试和自动化。支持多种编程语言 API&#xff0c; 包括 JavaScript 、 TypeScript, Python, .NET 和 Java。正因为它基于浏览器&#xff0c;相…

作者头像 李华
网站建设 2026/6/22 10:03:33

论文查重降重难题如何破解?知网AI率高怎么办?实用【嘎嘎降AI】与【比话降AI】对比指南

高校毕业季&#xff0c;论文查重和AI率检测成了影响顺利毕业的关键环节。知网AIGC检测对论文AI率的严苛审核&#xff0c;给不少同学带来降重压力。结合真实使用感受&#xff0c;本文细致分析论文降重、查AI率的常见难题&#xff0c;重点介绍两款业界口碑降AI工具——【嘎嘎降AI…

作者头像 李华
网站建设 2026/6/17 0:54:32

CDN加速推荐

白山云科技CDN概述白山云科技&#xff08;BaishanCloud&#xff09;是一家专注于边缘计算和内容分发网络&#xff08;CDN&#xff09;服务的云服务提供商。其CDN服务通过全球分布的边缘节点&#xff0c;帮助用户加速内容分发、降低延迟&#xff0c;并提升终端用户的访问体验。核…

作者头像 李华
网站建设 2026/6/28 20:02:29

多模态探索:快速搭建Z-Image-Turbo与语言模型联合创作环境

多模态探索&#xff1a;快速搭建Z-Image-Turbo与语言模型联合创作环境 如果你是一名AI研究者&#xff0c;想要探索图像生成与语言模型的协同创作潜力&#xff0c;但苦于整合不同AI系统的技术门槛太高&#xff0c;那么这篇文章正是为你准备的。本文将介绍如何利用预配置好的多模…

作者头像 李华