想象你开了一家餐厅,后厨要同时管理两套“描述系统”。一套是《员工手册》(interface):每个岗位都有一张固定表格,写着姓名、工号、入职日期,表格必须规规范范,因为它是用来约束人和流程的。另一套是贴在灶台上的《配方卡》(type):它更像是便利贴——“招牌酱汁 = 生抽 + 蜂蜜”,可以极其简短,口味还能千变万化。TypeScript(简称 TS,一种给 JavaScript 加类型检查的语言)里 interface 与 type 的关系,就像员工手册与配方卡。它们都能描述“一样东西长什么样”,但手册的规矩是“补充、合并、继承”,配方卡的看家本领是“混合、计算、定制”。很多开发者纠结于“interface vs type 怎么选”,其实只需要记住:先分清你要描述的是“一份岗位表”,还是一张“可灵活调配的配方”。
对 AI 从业者的价值:AI 工程里无论写 Agent 工具函数、RAG 检索器还是模型服务的入参出参,都要用 TS 定义类型。选择谁更合适,直接决定库的可扩展性和团队协作效率——面试常考,写代码天天用。
可视化演示
双栏对比展示TypeScript中interface与type的核心差异:语法能力、声明合并、扩展方式和最佳实践,切换维度查看每个方面的支持情况和适用场景。
本节配有交互动效(进入视口后自动加载,约 3KB)。
语法差异对比 interface擅长描述对象形状和类结构,支持extends扩展 type支持联合、交叉、条件、元组、映射等高级类型运算 type = interface ∪ {union, intersection, conditional, tuple, mapped} 语法:type能力范围更广
基本语法差异
餐厅的《员工手册》里,每个岗位都有一张固定表格:姓名栏、工号栏、入职日期栏。表格必须规规范范,因为“收银员”这个岗位长什么样,由表格说了算。而灶台边的《配方卡》只是便利贴:写着“招牌酱汁 = 生抽 + 蜂蜜”,它本身可以是任何东西的简短代号。interface 与 type 的第一道分水岭,就在这“表格”与“便利贴”之间。
在 TypeScript 里,interface 用一对花括号描述“对象应该长成什么样”,语法就像填表:``interface User { name: string; age: number }``。而 type 用等号,右边不一定是对象,可以是字符串、数字、数组、函数签名等任何已有类型。换句话说,interface 天生是“对象的模具”,type 天生是“任意类型的别名”。
两者的思维导向因此不同。如果你写 ``type UserID = string | number``,一眼就知道 UserID 可能是字符串也可能是数字,这种“或”的表达 interface 做不到;反过来,如果只是想表达“一个 User 对象有 name 和 age 两个字段”,interface 的表格感更直白。很多团队据此定下第一条规则:描述对象形状优先 interface,描述“一个任意类型的快速叫法”用 type。
所以,第一步不是比谁更强大,而是看素材像什么:要画一张岗位表就翻开员工手册,要写一张便签就抽出配方卡。先选对“描述工具”的类型,后面的烦恼会少一半。
- interface 关键字声明“这是一张图纸,用来规定对象长什么样”。
- 花括号内容里面的每一项都是“属性名:类型”,表示 User 必须有 name 和 age 两个字段。
- type 关键字与竖线type 用
=绑定右边的类型表达式,|表示“或”,所以 UserID 要么是 string 要么是 number。
声明合并特性
《员工手册》是活页本。上午在“收银员”表格里补一行“需会扫码”,下午又补一行“需会开发票”,最终定稿时,所有补充项会自动汇成一份完整的岗位说明,谁也不用覆盖谁。但《配方卡》是印刷卡片:如果你印了两张同样叫“招牌酱汁”的卡片,后厨只会以为你印错了——两张卡没法自动并成一张。
这正是 interface 与 type 最令人印象深刻的分歧点。在 TS 中,同名 interface 可以出现多次,编译器会把它们的所有属性合并成一个完整接口,这叫声明合并(declaration merging)。而同名 type 一旦重复声明,直接报"Duplicate identifier"(重复标识符)错误,绝不会悄悄合并。
这个特性最实用的场景是“补充别人定义好的类型”。比如浏览器全局对象 Window 由官方声明,但你想给它挂一个自己的全局变量,就可以在自己的 .d.ts 文件里再写一个 ``interface Window { myGlobal: string }`,TS 会自动把 myGlobal` 并进官方 Window 类型,像在别人的手册上合法加页。
活页本的价值在于“持续补充而不破坏原页”。interface 的声明合并让团队可以安全地给第三方类型追加字段,而 type 的“一锤定音”则保证了配方卡的纯净——这是两种完全不同的取舍哲学。
- Box 出现两次同名 interface 允许重复书写,这是 type 做不到的。
- 加法符号 ⊕表示把两次声明的所有属性取并集合并。
- 箭头结论合并后得到一个包含全部属性的 Box,编译时自动完成。
type独有能力
如果说 interface 是一本装订好的手册,那 type 就是一张可以任意调配的配方卡。后厨的利器在于“混合”:把鸡尾酒配方写成“威士忌或朗姆”,这是联合类型(要么 A 要么 B);把甜品配方写成“既要是蛋糕又要能冷藏”,这是交叉类型(A 和 B 都要);还有“如果客人能吃辣就上麻婆豆腐,否则上清蒸鱼”的条件类型,以及“把一张菜单批量改成英文版”的映射类型。这些配方级操作,员工手册一概做不到。
在 TS 里,这些能力全部由 type 扛起。联合类型写 string | number,交叉类型写 A & B,条件类型写 T extends U ? X : Y,映射类型写 [K in keyof T]。interface 的语法结构是“属性名:类型”的列表,天生无法表达“这个对象要么整个人出现,要么只出现一部分”这种灵活性。元组(Tuple,固定长度数组)如 [string, number] 也必须借助 type。
正因为 type 支持这些“类型计算”,它成了写工具类型(Utility Types)的舞台。比如 TS 内置的 Partial<T>(把 T 全部属性变成可选)就是映射类型的经典产物。你可以用 interface 描述“一个已知对象长得怎样”,但描述“一个对象经过某种变换之后长什么样”,只能靠 type。
配方卡的灵魂是“变化”。当你面对的数据形态多变——例如大模型的输出“要么是文本,要么是结构化 JSON,要么是工具调用”——type 的联合与条件能力就是唯一顺手的工具。记住:想描述“一个固定的对象”,找手册;想做“类型之间的运算”,找配方。
- 集合符号 ∪表示 T 是“A 或 B”,这就是联合类型。
- 集合符号 ∩表示 U 必须同时具备 A 与 B 的全部属性,这就是交叉类型。
- 条件表达式如果 T 能赋值给 U,结果取 X,否则取 Y,相当于类型的"if-else"。
扩展方式区别
员工手册的扩展方式是“继承”:《主管手册》 extends《员工手册》,意思是主管自动拥有员工的一切职责,再额外加上管理条款,这是一种自上而下的等级关系。配方卡的扩展方式是“混合”:把红烧肉配方和土豆配方混在一起,得到“土豆烧肉”,两种材料平起平坐,谁也不是谁的上级。
在 TS 中,interface 用 extends 关键字继承另一个 interface 或 class,继承后自动获得父级全部属性,再追加新属性。type 没有 extends 语法,它用交叉类型 A & B 把多个类型组合成一个整体。这两种扩展的哲学完全不同:extends 强调“从哪来、上溯到谁”,& 强调“由哪些部分拼装而成”。
这里有一个经典陷阱:interface 能用 extends 去扩展一个 type,但前提是这个 type 必须是一个对象类型,比如 ``type Animal = { name: string }`。一旦 type 是联合类型(如 string | number),interface extends 会直接报错,因为手册无法继承一张"要么威士忌要么朗姆"的配方卡。反过来说,type 却可以放心地用交叉类型组合一切:`type Mixed = User & Admin`` 毫无压力。
扩展方式的选择,本质上是选择“层级制度”还是“拼装文化”。面向对象风格的项目里,员工手册(interface)的继承关系清晰可查;函数式或数据密集型代码里,配方卡(type)的交叉组合更自由。如果你发现一个类型需要“继承一个联合类型”,那答案只有一个:放下 interface,拿起 type。
- extends 关键字Child 继承 Parent 的全部属性,并允许继续添加新字段。
- 与符号 &把 A 和 B 的所有属性合并成 Mixed,逻辑上等价于“同时满足 A 与 B”。
- 限定关系interface extends 只能接对象类型的 type,不能接联合类型。
索引签名场景
《员工手册》最后一页是“附录索引表”:登记着员工的编号规则——“编号:字符串,值:员工姓名”。有了这张索引表,任何符合规则的员工记录都能被快速查找。配方卡也有类似设计:一个“任意调料格”,写着 [调料名: string]: 用量,方便后厨动态补充新调料,而不必为每种调料单独写一张卡。
在 TS 中,interface 和 type 都能定义索引签名(Index Signature),写法类似 [key: string]: unknown,表示“这个对象的键是字符串,值是 unknown 类型”。但两者的差异藏在细节里:interface 如果多次声明同一个名字,每次声明的索引签名类型必须保持兼容一致,否则合并时直接报错;type 因为没有声明合并,自然没有这个烦恼。
还有一个容易被忽视的规则:interface 里一旦定义了索引签名 [key: string]: string,那么它内部的普通属性(如 age: number)就必须兼容 string 类型,否则报错——因为 age 也会被当成索引的一部分。type 虽然也遵守这个规则,但它的优势在于没有“重复声明合并”的额外约束,组合多个带索引签名的类型时更加自由。
索引签名的场景在 AI 工程里非常常见:当你把一整个配置文件或模型返回的字典对象接进来时,往往希望“键值自由但类型统一”。遇到这种“附录索引表”式的需求,两者都能做;但如果涉及多个带索引签名的类型要合并,type 的自由度更高,而 interface 则更适合“固定一张表、不许乱合并”的场景。
- 方括号 key
[key: string]表示对象的键是字符串,这叫索引签名。 - unknown 值值类型为 unknown,即“先不限定,用时再收窄”。
- K in stringtype 用更接近集合遍历的写法表达同样的意思。
性能与产物
宴会结束后,员工手册和配方卡都要收进档案柜——它们只是“描述”,不是菜品本身。TS 也一样:编译成 JavaScript 后,interface 和 type 统统消失得干干净净,不留任何运行时代码。你写一万行类型定义,打包出来的 JS 文件里一个都找不见。
既然如此,为什么官方 wiki 还提到"Prefer Interfaces Over Intersections"(优先接口而非交叉类型)?核心原因是类型缓存:interface 的合并结构可以被编译器缓存,多次引用时直接复用;type 的交叉类型(特别是复杂的大交叉)每次使用都可能重新展开、重新计算,在大规模代码库里会拖慢编译速度。这就像手册每次引用都直接翻档案库,而配方卡每次都要重新称量材料。
需要说明的是,这个性能差异对绝大多数项目来说微乎其微,真正让 TS 编译变慢的往往是深层的条件类型和递归类型(这些必须用 type 写)。所以结论是:不用为了性能而恐慌性地避开 type,但要理解“interface 更轻、type 更重”的底层原因,在超大项目中把对象描述尽量交给 interface。
理解了“编译后蒸发”,你就不会再把 interface 和 type 的差异想成“运行时性能之争”。“类型是编译期的私事”这个认知,是写出高质量 TS 代码的前提。
- TS 源码编译前的 TypeScript 文件包含类型标注。
- 编译箭头经过编译器处理,输出 JavaScript。
- 差集符号输出的 JS 中“减去”所有类型信息,interface 和 type 都无影无踪。
社区最佳实践
每个行业都有约定俗成的规矩:中餐后厨里,切墩的用菜刀、片鸭的用片刀,不是说不能用别的刀,而是大家默认这么分工,协作效率最高。TypeScript 社区也形成了这样的“用刀默契”——官方手册明确建议:优先使用 interface,直到你需要 type 的特性。
这条建议的落地非常明显。React 官方类型定义中,组件 props 和 state 几乎全部用 interface 描述,因为它们代表“组件对外承诺的形状”,需要可继承、可合并;而函数重载的类型签名、工具类型内部、联合类型,则大量使用 type。DefinitelyTyped(社区维护的类型仓库)里同样遵循这个惯例:公共 API 的对象结构用 interface,内部计算的复杂类型用 type。
eslint 也助推了这一共识:@typescript-eslint/consistent-type-definitions 规则默认要求“能用 interface 就用 interface,否则用 type”。这意味着在规范的团队里,代码会形成一种整齐的韵律:读一个类型定义,先看到 interface 就知道它是“契约”,看到 type 就知道它是“计算”。混用不是报错,但会破坏这种韵律感,让阅读者每次都要停下来猜。
行业惯例不是法律,但它是无数项目踩坑后的总结。跟着社区走,你的代码会让同事感到熟悉和安全:看到 interface 安心它可扩展,看到 type 知道它是拿来算的。一致的风格本身就是一种可维护性。
- 全称量词 ∀对于所有“对象形状”的场景。
- prefer interface都优先选择 interface,这是官方手册的第一原则。
选择建议总结
给后厨贴一张“点餐决策卡”:要描述岗位→翻员工手册;要调配新菜→抽配方卡;要补充规则→往手册加页;要做特殊混合→拿配方卡做文章。TS 类型选择同样可以浓缩成一张决策卡,让你五秒内做出判断。
具体规则如下:需要描述一个对象形状(比如 AI 函数的入参对象、后端返回的用户数据)→ 用 interface;需要声明合并(给全局对象补字段、设计可扩展的插件 API)→ 用 interface;需要定义联合类型、交叉类型、条件类型、映射类型或元组 → 用 type;需要给基础类型或函数签名起别名 → 用 type;面向公共 API 和开源库 → 对象类型首选 interface,复杂的工具类型用 type 并向外部暴露明确的定义。
如果还是拿不准,记住官方手册的原话:“当你不确定时,用 interface,直到 type 能解决你遇到的问题。” 这句话的精髓不是贬低 type,而是告诉你:interface 是默认选项,type 是精确制导武器。团队协作中,比选哪个更重要的是“统一”,因此把这张决策卡写进项目的 README,让所有人都按同一套逻辑选择,远比争论“某一次该用谁”更有价值。
这张决策卡就是全文的落点。员工手册和配方卡没有谁取代谁,它们服务于不同的厨房场景。你现在需要的不是记住每一条语法细节,而是带着这张决策卡回到你的项目里,随手翻开一个类型定义——问自己:这是表格,还是配方?答案自然浮现。
- 分段函数pick 结果由场景决条件决定,两个分支互不重叠。
- 第一个分支如果目标是对象形状、声明合并或面向对象继承,选 interface。
- 第二个分支如果目标是联合、元组、条件或映射类型,选 type。
AI 应用场景
给 AI 工具库设计公共 API 类型
阶段:Agent 工具链开发 / SDK 封装
解决的问题:写一个供其他开发者调用的 AI 函数库时,如果入参出参用 type 定义对象,使用者无法通过声明合并追加字段,只能 fork 源码修改;改用 interface 定义参数对象,下游开发者就能在自己的命名空间里安全扩展。
具体案例:你发布了一个 RAG 检索器 SDK,方法签名是 ``interface RetrieverInput { query: string; topK: number }`。某大型团队接入时想追加一个 metadata 字段,他们只需在自己的 .d.ts` 中再次声明同名 interface 并补充字段,无需改动你的源码。
定义 LLM Function Calling 的输入 Schema
阶段:模型服务接入 / 工具参数解析
解决的问题:大模型工具调用(Function Calling)的入参常常是“要么文本、要么图片、要么结构化参数”的多选形态,用 interface 无法表达;type 的联合和元组类型可以精准描述,并自动推导出 JSON Schema。
具体案例:一个智能客服 Agent 的 send_message 工具入参被定义为 ``type MsgInput = { type: 'text'; content: string } | { type: 'image'; url: string } | { type: 'tool_call'; toolId: number }``,LLM 返回的参数经过类型收窄后,代码分支每个字面量类型都能获得自动补全。
在 d.ts 声明文件中混用两者
阶段:前端基础设施 / 类型环境搭建
解决的问题:项目的全局类型既要能被任意文件补充(interface 合并),又要复用好几个复杂工具类型(type 计算)。混用让每种工具待在最适合的位置。
具体案例:在一个 AI 标注平台的前端里,``interface Window { __labEnv: 'prod' | 'staging' }` 负责全局注入,而 type PartialBy<T, K>` 这类工具函数用 type 编写,实现了“契约归契约,算法归算法”的划分。
Python 演示
"""模拟 TS 中 interface 与 type 的核心行为差异"""class TypeRegistry: """一个迷你类型登记处,模拟 TS 编译器的类型收集过程""" def __init__(self): self.interface_map = {} # 名字 -> 合并后的属性字典 self.type_alias_map = {} # 名字 -> 类型表达式 def declare_interface(self, name, props): # 模拟 interface 声明合并:同名接口自动合并属性 prev = self.interface_map.get(name, {}) self.interface_map[name] = {**prev, **props} return dict(self.interface_map[name]) def declare_type(self, name, value): # 模拟 type 声明:同名 type 直接报错 if name in self.type_alias_map: raise ValueError("type " + name + " 重复声明") self.type_alias_map[name] = value def union(self, *names): # 模拟 type 的联合类型组合 return [self.type_alias_map[n] for n in names] def main(): reg = TypeRegistry() # interface 可以多次声明,自动合并成一份 part1 = reg.declare_interface("User", {"id": "string"}) part2 = reg.declare_interface("User", {"name": "string"}) print("interface 合并结果:", part1, part2) # type 只能声明一次 reg.declare_type("Status", "string | number") try: reg.declare_type("Status", "boolean") except ValueError as exc: print("type 重复声明报错:", exc) # type 擅长组合出联合类型 print("type 联合组合:", reg.union("Status")) if __name__ == "__main__": main()
self.interface_map = {}用字典存放接口合并结果,键是类型名,值是属性集合。def declare_interface(self, name, props)对应 TS 的同名 interface 声明行为。self.interface_map[name] = {**prev, **props}把旧属性和新属性合并成一个新字典,模拟“活页加页”。if name in self.type_alias_map: raise ValueError(...)同名 type 直接抛异常,模拟“重复标识符”错误。def union(self, *names)把多个 type 表达式收集进列表,模拟联合类型的“多选一”思想。
常见误区
误区“interface 的语法是 `{}`,那它只能描述普通对象,函数类型、数组、元组这些只能用 type 写。”
正解interface 也能描述函数类型,例如 ``interface Fn { (x: number): string }` 等价于 type Fn = (x: number) => string;它还能通过索引签名描述数组或字典。只不过语法上 {}` 让人产生“只能描述对象”的错觉。但确实,联合类型和元组是 interface 只能借用 type 才能写出来的。
为何常见:新手从“interface 用花括号”这个表面特征出发,推导出“只能描述对象”的错误结论,忽略了 interface 内部的多种结构能力。
误区“interface 能声明合并,那我把 type 也写两遍,它是不是也会自动并成一个类型?”
正解不会。同名 type 一旦重复声明,TS 会直接报 Duplicate identifier 错误。能合并的只有 interface(以及 interface 与同名 namespace 的部分合并)。type 的“重复”是错误,interface 的“重复”是设计。把 type 当 interface 用,等于把活页本和印刷卡混为一谈。
为何常见:先学完 interface 的合并特性,很容易把这种“便利”泛化到 type 上,而没有注意到两者的底层设计完全不同。
误区“type 是做类型计算的,它会让 TS 编译变慢,所以项目中尽量用 interface,少用 type。”
正解编译速度的差异几乎不来自“type 这个关键字”,而来自复杂条件类型、递归类型这类高计算量表达式——它们只能写在 type 里。interface 的缓存优化在大型项目里确实有价值,但普通项目可忽略。用 type 写联合类型不会拖慢编译,写千层递归的 DeepPartial 才会。
为何常见:把“复杂类型计算慢”这个现象,错误地归因为“type 关键字慢”,基于表象做了过度概括。
误区“interface 都能继承 type 了,说明 interface 才是完全体,任何时候都该选它。”
正解interface extends type 有一个严格前提:type 必须是对象类型。如果 type 是 string | number 这样的联合类型,extends 直接报错。反过来,type 用交叉类型 & 可以组合包括联合、元组、函数在内的任何类型,自由度反而更高。两者各有边界,不存在谁完全包含谁。
为何常见:看到了“interface extends type“的用法后,误以为这是面向对象继承关系的扩展,没意识到它只是”把对象属性复制一遍”这种受限操作。
误区“编译后都消失了,功能上也差不多,我这次用 interface,下次用 type,反正编译器不报错。”
正解功能上大多等价,但可维护性与协作语义差别很大。interface 表示“可扩展的契约”,type 表示“已算好的配方”。混用会让读者每次都要停下来猜意图,也破坏了公共 API 的可扩展性——如果对象类型用 type 定义,外部团队想通过声明合并追加字段时毫无办法。团队规范的核心是“一致性”。
为何常见:小项目里混写从不报错,于是忽略了代码对“人”的交流价值,只有当多人协作踩坑后才会痛。
面试题
Q1. 关于同名声明合并,以下说法正确的是?
- A.interface 和 type 都会自动合并
- B.只有 interface 会自动合并← 正确答案
- C.只有 type 会自动合并
- D.两者都不会自动合并
答案:B。interface 设计上支持声明合并,多个同名声明自动并成一份;type 同名会报“重复标识符”错误。这就像员工手册可以加页,而复制的配方卡只会造成冲突。
Q2. 要定义一个“字符串或数字”的类型,应该优先选哪个关键字?
- A.interface 关键字
- B.type 关键字← 正确答案
- C.class 关键字
- D.enum 关键字
答案:B。联合类型 string | number 是 type 的独有能力,interface 无法直接表达“要么 A 要么 B”这种结构。type 的等号语法天生适合右边写任意类型表达式。
Q3. interface Admin extends User 表达的正确语义是?
- A.Admin 完全覆盖 User 的定义
- B.Admin 继承 User 全部属性并可继续新增← 正确答案
- C.Admin 与 User 的关系变成联合类型
- D.Admin 只是 User 的一个别名
答案:B。extends 是纵向继承,Admin 自动获得 User 的所有属性,再追加自己的新属性。它不是覆盖、不是联合、也不是别名,而是“在父级基础上扩展”,类似主管手册继承员工手册条款。
Q4. type Mixed = { a: number } & { b: string },Mixed 的类型是?
- A.要么只有 a 属性
- B.同时拥有 a 和 b 属性← 正确答案
- C.只有 a 没有 b 属性
- D.a 和 b 属性互斥出现
答案:B。& 是交叉类型,表示同时满足左右两边,所以 Mixed 必须同时拥有 a 和 b。这就像“土豆烧肉”必须既有土豆又有烧肉,而不是二选一。
Q5. TypeScript 编译为 JavaScript 之后,interface 和 type 会怎样?
- A.变成 JS 对象
- B.变成函数
- C.完全消失← 正确答案
- D.保留为注释
答案:C。类型信息只存在于编译期,编译后所有 interface 与 type 都会被擦除,JS 产物里找不到任何痕迹。它们只负责“编译时检查”,不参与运行时逻辑。
Q6. interface 多次声明相同名字时,每次声明的索引签名类型必须如何?
- A.可以完全不同
- B.后者覆盖前者
- C.保持兼容一致← 正确答案
- D.必须都用 number
答案:C。interface 的声明合并要求里,索引签名需要兼容一致,否则合并时报错。比如一次写 [key: string]: string,另一次写 [key: string]: number,TS 会拒绝合并。而 type 没有合并场景,自然不受此限制。
Q7. 以下哪种 type 无法被 interface extends 扩展?
- A.type Animal = { name: string }
- B.type A = string | number← 正确答案
- C.type B = { age: number }
- D.type C = { list: string[] }
答案:B。interface extends 只能扩展“对象类型的 type”,不能扩展联合类型。string | number 是联合类型,不是一个有属性的对象,无法被 extends。其他选项都是对象形状,可以被继承。
Q8. 以下哪些是 type 独有的表达能力?
- A.联合类型← 正确答案
- B.声明合并
- C.条件类型← 正确答案
- D.元组← 正确答案
答案:ACD。联合类型、条件类型、元组(如 [string, number])都无法用 interface 直接表达,只能写在 type 里。声明合并是 interface 的独有能力,type 同名会报错。这三项加一项合并,正好是两者的核心能力分界线。
Q9. 关于 interface extends type,以下说法正确的有?
- A.只能扩展到对象类型的 type← 正确答案
- B.不能扩展联合类型← 正确答案
- C.type 被扩展后可以反向修改
- D.扩展后类型会自动合并
答案:AB。interface extends type 只是在编译期把 type 的属性“复制”进 interface;type 本身不会改变,不存在“反向修改”;这也不是声明合并,而是继承语义。如果 type 是联合类型则直接报错,所以 A、B 正确。
Q10. 在实际项目中,关于 interface 与 type 的差异,下列描述正确的有?
- A.interface 可在 d.ts 中通过声明合并补充全局类型← 正确答案
- B.type 的交叉类型遇到同名属性时编译会直接报错
- C.interface extends type 时 type 可以是联合类型
- D.官方推荐“先 interface,不够再用 type”← 正确答案
答案:AD。A 是声明合并的经典用法,全局类型如 Window 可被多次 interface 补充;B 错误,交叉类型遇到同名属性不直接报错,而是把属性变成 never 或难以赋值,错误在真正使用该属性时才暴露;C 错误,interface 不能 extends 联合类型;D 正是 TS 官方手册的原话。