TypeScript类成员声明中{[x:string]:this}与Record<string,this>为何不等价?
上周我在Stack Overflow上提问了一个关于如何声明参数与类协变的类成员函数的问题,使用多态this类型是完美的解决方案。
我的实际代码中有一个类,其成员是这类函数的字典。在适配该方案时,我遇到了意外的类型错误。
给定如下类声明:
class C { public dict1: {[x: string]: this} = {}; // Error: A 'this' type is available only in a non-static member of a class or interface. public dict2: Record<string, this> = {}; // OK public dict3: {[P in string]: this} = {}; // OK?! }
为何使用索引签名的dict1会触发错误:
A 'this' type is available only in a non-static member of a class or interface
而看似等价的、使用Record<>工具类型的dict2,以及基于Record<>定义的索引签名的dict3却不会触发该错误?
这是TypeScript类型系统对不同语法结构的规则差异导致的,核心区别在于常规索引签名和映射类型的this解析逻辑不同:
对于
dict1的常规索引签名{[x: string]: this}:TypeScript编译器将这种直接定义的索引签名视为静态结构,不会在这里启用多态this的处理规则,因此会抛出错误——编译器没有识别出该索引签名需要和类的多态this绑定,只会按普通静态类型规则校验。对于
dict2的Record<string, this>和dict3的{[P in string]: this}:二者本质都是映射类型。映射类型是通过遍历键集合动态生成的类型结构,TypeScript编译器会对映射类型内部的this进行特殊处理,将其识别为类的多态this类型,允许在这种上下文里使用,因此不会触发错误。
其实Record<K, T>的底层定义就是映射类型{[P in K]: T},所以dict2和dict3的行为完全一致,都能合法使用this类型。
内容的提问来源于stack exchange,提问作者cpcallen

