typescript 将第二个重载添加为对象会产生两个错误

2nc8po8w  于 2023-02-10  发布在  TypeScript
关注(0)|答案(1)|浏览(91)

我试着创建一个重载函数,一个有两个参数,另一个有一个参数作为对象。它们的用法如下:

// As two parameters
obj.set('a', '123');
obj.set('b', 'efg');

// As a single object parameter
obj.set({ a: '123', b: 'efg' });

然后,我声明重载如下:

export type Fields<T extends BaseModel> = { [K in keyof T['fields']]: T['fields'][K] | typeof BaseModel };

export abstract class BaseModel {
  abstract fields: Fields<any>;

  // This overload works
  set<K extends keyof Fields<this>>(field: K, value: Fields<this>[K]): this;
  
  // This overload throws an error
  set<K extends keyof Fields<this>>(fields: { [key: keyof Fields<this>]: Fields<this>[K] }): this;
}

我的单参数overload(作为一个对象)显示这个错误:
“this”类型仅在类或接口的非静态成员中可用。

bq3bfh9z

bq3bfh9z1#

问题是索引签名和mapped types看起来很相似,并且可能具有某种相似的效果,但是它们具有不同的规则和不同的效果。
索引签名可以是任何对象类型以及其他属性和签名的一部分,其语法如下

type IndexSig = { [dummyKeyIdentifier: KeyType]: ValueType }

其中dummyKeyIdentifier是一个 * dummy * 密钥标识符,因为它仅用于文档目的,并且对于类型系统是不可观察的。对于KeyType允许是什么存在相当严格的规则;几乎只有stringnumbersymbol、"pattern"模板文本类型以及这些类型的联合。在示例代码中,不能使用"a"3等字符串/数字文本类型,也不能使用Kgeneric type,或涉及表现得像隐式泛型类型参数的多态this类型的任何类型。
因此代码中出现错误。
另一方面,Map类型是独立类型(在大括号内不能存在其他属性或签名),其语法如下

type MappedType = { [K in KeyType]: ValueType<K> }

其中K是一个 * 泛型类型参数 *,它迭代KeyType的联合成员,并且在属性类型的范围内。因此,虽然在索引签名中不能再次提及dummyKeyIdentifier,但可以在Map类型中使用K,以便为KeyType中的每个键执行不同的操作。
在这里,KeyType的规则相当宽松;你仍然可以使用stringnumbersymbol和模式模板常量(尽管这些最终被转换成了等价的索引签名),但是你也可以使用常量类型和泛型类型以及它们的联合。
您可以识别它们之间的区别,因为索引签名在方括号内有冒号(:),而Map类型有in关键字。
编译器经常会注意到你在以一种被禁止的方式使用索引签名,并建议你使用Map类型。有时编译器没有意识到Map类型会有帮助,但这仍然是潜在的问题。
在您的情况下,如果将索引签名更改为Map类型:

declare abstract class BaseModel {
  abstract fields: Fields<any>;
  set<K extends keyof Fields<this>>(field: K, value: Fields<this>[K]): this;
  
  set<K extends keyof Fields<this>>(
    fields: { [P in keyof Fields<this>]: Fields<this>[K] } // okay
  ): this; 
}

一切都开始运转。
Playground代码链接

相关问题