我拥有的
我在一个类中有一个方法,我想接受任何对象作为参数,但我不想使用 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
的值是痛苦的。并对其属性做任何有用的事情。如果能解决这个问题就太好了。但是,类型object
以 Record<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
}
我仍然会说,除非
object
在 saveObject()
的实现中给你一些具体的问题,为那一行禁用 linter 并使用 object
是合理的。反而。它比 Record
更能表达“任何非原始”是。根据 linter,不使用 object
的假设原因就是难用。那是真实的;但它很容易供应,如果您想调用this.saveObject()
在比您想要实现的更多地方,我宁愿在实现中做一件烦人的事情,而不是在每个调用站点上做很多烦人的事情。Playground link to code
关于typescript - 接受任何对象作为函数中的参数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/66770560/