为什么TypeScript中interface顶层无法直接使用映射类型定义属性
问题描述
请看如下枚举定义:
enum Numbers { ONE=1, TWO=2 }
以下interface定义会抛出编译时错误:
interface Config { [n in Numbers]: string; }
报错信息如下:
- 接口中的计算属性名引用的表达式类型必须是字面量类型或
unique symbol类型。- 计算属性名必须为
string、number、symbol或any类型。- 找不到名称
n。
但如果使用type类型别名定义:
type Config = { [n in Numbers]: string; }
或是将映射类型放到接口定义的更深层级:
interface Config { a: { [n in Numbers]: string; }; }
代码都可以正常编译。请问为什么interface的顶层属性不能使用映射类型定义?
原因解释
这完全是TypeScript本身的语法设计规则导致的,核心差异在interface和type的定位,以及映射类型的适用范围:
- 首先要明确,
[K in 联合类型]这种遍历生成属性的写法,是映射类型专属的语法,只有你把整个结构直接作为类型定义赋值给type别名的时候,TS才会把它识别成映射类型。 - 而
interface的顶层属性声明根本不支持这种动态映射的语法,它只允许两种顶层属性声明:要么是写死的固定属性名,要么是普通索引签名(就是[key: string]: any、[key: number]: any这种,只能声明key的类型,不能遍历指定的联合/枚举)。 - 你在interface顶层写
[n in Numbers]的时候,TS根本不会把它当成映射语法处理,只会当成普通的JS计算属性名来解析,当然会报“找不到变量n”、“计算属性类型不符合要求”这些错。 - 至于嵌套在interface内部的映射类型可以正常用,是因为这时候你写的
{[n in Numbers]: string}已经不是interface的顶层成员声明了,它只是属性a的值类型,作为普通的类型表达式用在哪都合法。 - type别名能用是因为它的作用就是给任意合法的TS类型起别名,映射类型本身就是合法的类型,当然可以直接声明。而interface从设计上就是要支持声明合并、类实现这类面向对象场景,要求它的顶层属性是静态可确定的,不能是动态遍历生成的,所以从设计之初就没打算支持顶层用映射类型。
内容的提问来源于stack exchange,提问作者Klaimmore
相关产品推荐
相关产品推荐

