如何在启用 no-unsafe-any 的情况下编写类型保护?
How to write type guards with no-unsafe-any enabled?
我试图通过使用更严格的 lint 规则集来加强我的 TS 代码,但我正在努力解决动态性的合法用途。
我正在制作一个类型保护来检测某些东西是否是可迭代的(如果不是,则将其包装在一个数组中),除了抑制 lint 规则以告诉它这是 kosher 之外,我不知道该告诉 TS 什么:
function isIterable(obj: any): obj is Iterable<unknown> {
return obj && typeof obj[Symbol.iterator] === 'function';
}
我尝试将其更改为:
function isIterable(obj: undefined | {[Symbol.iterator]?: unknown}): obj is Iterable<unknown> {
return !!obj && typeof obj[Symbol.iterator] === 'function';
}
无需使用 any
即可编译,但它没有用,因为我想将未知类型的值传递给它。
是否有 "clean" 表达 "yes I actually want to rely on JS returning undefined
for accessing a property that doesn't exist on an object" 的方式?特别是因为这就是编写类型保护的全部意义。
我不知道在 user-defined type guard 的 实现 中,像 no-unsafe-any 这样的东西是否会让你买太多,因为通常这样的重点类型保护是允许编译器通过内置的控制流缩小来缩小它通常不能做的值。我当然理解在这样的实现中暂停 linter 规则。
但我认为您几乎可以像这样获得您正在寻找的行为:
function isIterable(obj: unknown): obj is Iterable<unknown> {
if ((typeof obj !== 'object') || (obj === null)) return false;
// obj is now type object
const wObj: { [Symbol.iterator]?: unknown } = obj; // safely widen to wObj
return typeof wObj[Symbol.iterator] === 'function';
}
这是一个需要跳过的环节,但我们的想法是使用控制流缩小将 unknown
缩小到 object
,然后将 object
专门扩大到具有可选类型的类型属性 您正在尝试检查(这是通过引入一个新变量发生的)。最后,检查加宽类型上 属性 的类型。由于要检查的 属性 键是符号类型,因此您需要在加宽类型中提及特定的 属性 名称。如果 属性 键是一个字符串,您可以使用 string index signature:
function isPromise(obj: unknown): obj is Promise<unknown> {
if ((typeof obj !== 'object') || (obj === null)) return false;
// obj is now type object
const wObj: {[k: string]: unknown} = obj; // safely widen to wObj
return typeof wObj.then === 'function';
}
无论如何,我希望这能让你更接近你的目标。祝你好运!
另一个好的策略是使用 Partial
和 as
转换。
interface RegularForm {
regular: number;
}
interface FancyForm extends RegularForm {
fancy: string;
}
const isFancyForm = (instance: RegularForm): instance is FancyForm =>
(instance as Partial<FancyForm>).fancy !== undefined;
我试图通过使用更严格的 lint 规则集来加强我的 TS 代码,但我正在努力解决动态性的合法用途。
我正在制作一个类型保护来检测某些东西是否是可迭代的(如果不是,则将其包装在一个数组中),除了抑制 lint 规则以告诉它这是 kosher 之外,我不知道该告诉 TS 什么:
function isIterable(obj: any): obj is Iterable<unknown> {
return obj && typeof obj[Symbol.iterator] === 'function';
}
我尝试将其更改为:
function isIterable(obj: undefined | {[Symbol.iterator]?: unknown}): obj is Iterable<unknown> {
return !!obj && typeof obj[Symbol.iterator] === 'function';
}
无需使用 any
即可编译,但它没有用,因为我想将未知类型的值传递给它。
是否有 "clean" 表达 "yes I actually want to rely on JS returning undefined
for accessing a property that doesn't exist on an object" 的方式?特别是因为这就是编写类型保护的全部意义。
我不知道在 user-defined type guard 的 实现 中,像 no-unsafe-any 这样的东西是否会让你买太多,因为通常这样的重点类型保护是允许编译器通过内置的控制流缩小来缩小它通常不能做的值。我当然理解在这样的实现中暂停 linter 规则。
但我认为您几乎可以像这样获得您正在寻找的行为:
function isIterable(obj: unknown): obj is Iterable<unknown> {
if ((typeof obj !== 'object') || (obj === null)) return false;
// obj is now type object
const wObj: { [Symbol.iterator]?: unknown } = obj; // safely widen to wObj
return typeof wObj[Symbol.iterator] === 'function';
}
这是一个需要跳过的环节,但我们的想法是使用控制流缩小将 unknown
缩小到 object
,然后将 object
专门扩大到具有可选类型的类型属性 您正在尝试检查(这是通过引入一个新变量发生的)。最后,检查加宽类型上 属性 的类型。由于要检查的 属性 键是符号类型,因此您需要在加宽类型中提及特定的 属性 名称。如果 属性 键是一个字符串,您可以使用 string index signature:
function isPromise(obj: unknown): obj is Promise<unknown> {
if ((typeof obj !== 'object') || (obj === null)) return false;
// obj is now type object
const wObj: {[k: string]: unknown} = obj; // safely widen to wObj
return typeof wObj.then === 'function';
}
无论如何,我希望这能让你更接近你的目标。祝你好运!
另一个好的策略是使用 Partial
和 as
转换。
interface RegularForm {
regular: number;
}
interface FancyForm extends RegularForm {
fancy: string;
}
const isFancyForm = (instance: RegularForm): instance is FancyForm =>
(instance as Partial<FancyForm>).fancy !== undefined;