接受任何对象作为函数中的参数
我拥有的
我在一个类中有一个方法,我想接受任何对象作为参数,但我不想使用any,为此我使用 ageneric和 extend object。
class MyClass {
saveObject<T extends object>(object: T | null) {}
}
有了这个实现,@typescript-eslint/ban-typesrule 会报以下错误:
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);
}
但是打字稿编译器抛出了一个新错误:
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-eslintban-types规则的默认配置的前提,即
避免使用该
object类型,因为目前由于无法断言密钥存在而难以使用。请参阅microsoft/TypeScript#21732。
作为提交链接问题的人,我确实理解尝试使用内置类型保护来获取类型的值object并对其属性做任何有用的事情是痛苦的。如果能解决这个问题就太好了。但是,该类型以一种object不代表“TypeScript 中的非原始值”的方式表示Record<string, unknown>。而且,正如您所注意到的,Record<string, unknown>它有其自身的问题,例如microsoft/TypeScript#15300。TypeScript 有很多陷阱和痛点,对我来说,一概反对一个而支持另一个的建议并不可取。
——
对于此特定用例,您可以从objectto{[k: string]: any}和 not切换{[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
}
}
(我已经更改了您的示例,使其不是通用的;这对于您的实际用例可能很重要,但作为代码示例,如果在任何地方都没有使用该通用性,则将函数设为通用是没有意义的。一个函数with 类型签名<T extends U>(x: T)=>void通常可以替换(x: U)=>void为没有不良影响)
这种增加的易用性并不是真正的类型安全,因为拥有类型的属性any类似于关闭类型检查。但是在 TypeScript 中有特殊的大小写,它允许任何对象可以分配给{[k: string]: any}但不能分配给{[k: string]: unknown}. 后一种类型禁止任何interface没有显式索引签名的类型(请参阅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 链接到代码