typescript - 接受任何对象作为函数中的参数

标签 typescript

我拥有的
我在一个类中有一个方法,我想接受任何对象作为参数,但我不想使用 any ,为此我使用 generic并延长 object .

class MyClass {
  saveObject<T extends object>(object: T | null) {}
}
通过这个实现,@typescript-eslint/ban-types规则提示以下错误:Don't use 'object' as a type. The 'object' type is currently hard to use see this issue(https://github.com/microsoft/TypeScript/issues/21732)). Consider using 'Record<string, unknown>' instead, as it allows you to more easily inspect and use the keys. ok,那我就听Eslint做如下实现:
class MyClass {
  saveObject<T extends Record<string, unknown>>(object: T | null) {}
}
通过上面的实现,Eslint 错误消失了,所以我尝试用 执行该方法随机对象 :
anyOtherMethodInMyCode(payment: IPaymentModel | null): void {
    // execute our method  
    this.saveObject(payment);
}
但是 typescript 编译器抛出了一个新错误 :
TS2345: Argument of type 'IPaymentModel | null' is not assignable to parameter of type 'Record<string, unknown> | null'.   
Type 'IPaymentModel' is not assignable to type 'Record<string, unknown>'. 
    Index signature is missing in type 'IPaymentModel'.
一种选择是使用 Type Assertion在传递给方法的参数中,如下所示:
anyOtherMethodInMyCode(payment: IPaymentModel | null): void {
    // execute our method with Type Assertion  
    this.saveObject(payment as Record<string, unknown>);
}
有了上述内容,TS 编译器错误消失了,但在执行方法的所有地方都必须这样做(类型断言)并不是最佳选择。
我想要的是
我不明白如何接受任何对象作为没有此类错误的参数,无需使用 any .

最佳答案

我不完全同意 typescript-eslint's ban-types rule默认配置的前提其中说

Avoid the object type, as it is currently hard to use due to not being able to assert that keys exist. See microsoft/TypeScript#21732.


作为提交链接问题的人,我确实理解尝试使用内置类型保护来获取类型 object 的值是痛苦的。并对其属性做任何有用的事情。如果能解决这个问题就太好了。但是,类型objectRecord<string, unknown> 的方式表示“TypeScript 中的非原始值”才不是。而且,正如您所注意到的,Record<string, unknown>有自己的问题,比如microsoft/TypeScript#15300 . TypeScript 有很多陷阱和痛点,对我来说,一概反对一个而支持另一个的建议并不可取。
——
对于此特定用例,您可以从 object 切换至 {[k: string]: any}而不是 {[k: string]: unknown} .如果您进行此更改,则处理 saveObject 中的值会更容易。 :
saveObject(obj: Record<string, any> | null) {
  if (obj === null) {
    console.log("nothing to save");
    return;
  }
  if (obj.format === "json") {
    // do something
  }
}
(我已经更改了您的示例,使其不是通用的;这对于您的实际用例可能很重要,但作为代码示例,如果在任何地方都没有使用该通用性,那么将函数设为通用是没有意义的。一个函数带有类型签名 <T extends U>(x: T)=>void 经常可以替换为 (x: U)=>void 没有不良影响)
这种增加的易用性并不是真正的类型安全,因为具有类型 any 的属性类似于关闭类型检查。但是在 TypeScript 中有特殊的大小写允许任何对象分配给 {[k: string]: any}但不是 {[k: string]: unknown} .后一种类型禁止任何 interface没有明确的类型 index signature (参见 microsoft/TypeScript#15300 ),而前者与 object 非常相似并且没有此限制(请参阅 microsoft/TypeScript#41746 )。
如果这对您有用(并且您没有使用 linting 来禁止 any ),那么您会发现事情会更好:
anyOtherMethodInMyCode(payment: IPaymentModel | null): void {
  this.saveObject(payment); // okay
}
otherTests(): void {
  this.saveObject("not an object"); // error!
  this.saveObject(() => 3); // okay, a function is an object
}

我仍然会说,除非 objectsaveObject() 的实现中给你一些具体的问题,为那一行禁用 linter 并使用 object 是合理的。反而。它比 Record 更能表达“任何非原始”是。根据 linter,不使用 object 的假设原因就是难用。那是真实的;但它很容易供应,如果您想调用this.saveObject()在比您想要实现的更多地方,我宁愿在实现中做一件烦人的事情,而不是在每个调用站点上做很多烦人的事情。
Playground link to code

关于typescript - 接受任何对象作为函数中的参数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/66770560/

相关文章:

javascript - 在Angular应用中使用ngx-monaco-editor在404结果

typescript - 如何在 Angular 2/Typescript 中声明一个全局变量?

javascript - 为什么 console.log() 创建/**id :4**/and/**ref:4**/values?

angular - Typescript:如何使用枚举来强制数组仅包含该枚举中的特定值?

javascript - 在 VScode 中,如何从导入文件的行打开文件?

angularjs - 使用 SystemJS 和 TypeScript 的 Angular 应用程序中的 "Unexpected token export"

javascript - 使用集合中获得的值,将包含该集合索引的数组中的值推送到单独的数组中

visual-studio - 如何在 Visual Studio 2017 中配置 TypeScript 和 Require

javascript - 如何使用 Validator 和 Control Value Accessor 正确实现嵌套表单?

typescript - 我的react-native expo 应用程序的已编译 typescript js 文件在哪里?