接受任何对象作为函数中的参数 [英] Accept any object as argument in function

查看:27
本文介绍了接受任何对象作为函数中的参数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I have a method in a class which I want to accept any object as an argument but I don't want to use any, for this I use a generic and extend object.

class MyClass {
  saveObject<T extends object>(object: T | null) {}
}

在这个实现中,@typescript-eslint/ban-types 规则抱怨以下错误:不要使用对象"作为类型.对象"类型目前难以使用,请参阅此问题(https://github.com/microsoft/TypeScript/issues/21732)).考虑使用 'Record'相反,因为它可以让您更轻松地检查和使用密钥. 好的,那么我将听 Eslint 并执行以下实现:

With this implementation, @typescript-eslint/ban-types rule complains with the following error: 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, then I will listen to Eslint and do the following implementation:

class MyClass {
  saveObject<T extends Record<string, unknown>>(object: T | null) {}
}

通过上面的实现,Eslint的错误消失了,所以我尝试用一​​个随机对象来执行该方法:

with the above implementation, the Eslint error disappears, so I try to execute the method with a random object:

anyOtherMethodInMyCode(payment: IPaymentModel | null): void {
    // execute our method  
    this.saveObject(payment);
}

但是打字稿编译器抛出了一个新错误:

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,如下所示:

One option is to use Type Assertion in the argument passed to the method as follow:

anyOtherMethodInMyCode(payment: IPaymentModel | null): void {
    // execute our method with Type Assertion  
    this.saveObject(payment as Record<string, unknown>);
}

使用上述方法,TS 编译器错误消失了,但在方法执行的所有位置都必须执行此操作(类型断言)并不是最佳选择.

With the above, TS compiler errors disappear but it is not optimal to have to do this(Type Assertion) in all places where the method is executed.

我不明白如何在不需要使用 any 的情况下接受任何对象作为参数而不出现此类错误.

I don't understand how to accept any object as an argument without having this kind of errors without the need to use any.

推荐答案

我不完全同意 typescript-eslint 的 ban-types 规则 说明

I don't completely agree with the premise of the default configuration of typescript-eslint's ban-types rule which says that

避免使用 object 类型,因为它目前由于无法断言键存在而难以使用.请参阅 microsoft/TypeScript#21732.

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 类型的值并对其属性做任何有用的事情是痛苦的.如果能解决这个问题就太好了.但是,类型 object 表示TypeScript 中的非原始值".以 Record 没有的方式.而且,正如您所注意到的,Record 有其自身的问题,例如 microsoft/TypeScript#15300.TypeScript 有很多陷阱和痛点,我认为不建议一概反对一个而支持另一个.

As the person who filed the linked issue, I do understand that it is painful to try to use built-in type guarding to take a value of type object and do anything useful with its properties. It would be great if this were fixed. However, the type object represents "a non-primitive value in TypeScript" in a way that Record<string, unknown> does not. And, as you've noticed, Record<string, unknown> has its own problems, such as microsoft/TypeScript#15300. TypeScript has lots of pitfalls and pain points, and a blanket recommendation against one in favor of another doesn't seem advisable to me.

--

对于这个特定用例,您可以从 object 切换到 {[k: string]: any} 而不是 {[k: string]:未知}.如果您进行此更改,将更容易处理 saveObject 中的值:

For this particular use case, you can switch from object to {[k: string]: any} and not {[k: string]: unknown}. If you make this change, it will be easier to process the value inside of saveObject:

saveObject(obj: Record<string, any> | null) {
  if (obj === null) {
    console.log("nothing to save");
    return;
  }
  if (obj.format === "json") {
    // do something
  }
}

(我已经更改了您的示例,使其不是通用的;这对于您的实际用例可能很重要,但作为代码示例,如果在任何地方都没有使用该通用性,则将函数设为通用是没有意义的. 具有类型签名 (x: T)=>void 的函数通常可以替换为 (x: U)=>void无不良反应)

(I've changed your example so that it is not generic; this may be important for your actual use case, but as a code example there's no point in making a function generic if that generic-ness isn't used anywhere. A function with type signature <T extends U>(x: T)=>void can very often be replaced with (x: U)=>void with no ill effects)

这种增加的易用性并不是真正的类型安全,因为具有 any 类型的属性类似于关闭类型检查.但是在 TypeScript 中有一个特殊的大小写,它允许将任何对象分配给 {[k: string]: any} 而不是 {[k: string]: unknown}.后一种类型禁止任何没有明确的 interface 类型>索引签名(参见microsoft/TypeScript#15300),而前者非常类似于 object 并且没有这个限制(见 microsoft/TypeScript#41746).

This increased ease of use is not really type safe, since having properties of type any are similar to turning off type checking. But there is special casing in TypeScript which will allow any object to be assignable to {[k: string]: any} but not {[k: string]: unknown}. The latter type prohibits any interface types without an explicit index signature (see microsoft/TypeScript#15300), while the former is very similar to object and does not have this restriction (see microsoft/TypeScript#41746).

如果这对您有用(并且您没有使用 linting 来禁止 any),那么您会发现事情会更好:

If that works for you (and you are not using linting to prohibit any), then you will find things working better:

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() 在比你想要的更多的地方实现,我宁愿在实现内部做一件烦人的事情,而不是在每个调用站点上做很多烦人的事情.


I would still say that unless object gives you some specific problem inside the implementation of saveObject(), it is reasonable to disable the linter for that one line and use object instead. It is more expressive of "any non-primitive" than Record is. According to the linter, the supposed reason not to use object is that it is hard to use. That is true; but it's easy to supply, and if you would like to call this.saveObject() in more places than you would like to implement, I'd rather do one annoying thing inside the implementation and not many annoying things at each call site.

游乐场链接代码

这篇关于接受任何对象作为函数中的参数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆