如何编写启用了no-unsafe-any的类型防护?

时间:2019-02-09 23:36:32

标签: typescript tslint

我正在尝试通过使用更严格的棉绒规则集来收紧我的TS代码,但我正在为应该合理使用动态性而苦苦挣扎。

我正在做一个类型保护措施,以检测某些事物是否可迭代(如果不是,则将其包装在数组中),除了抑制皮棉规则以告知这是犹太洁食之外,我不知道该如何告诉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即可进行编译,但它没有用,因为我想将未知类型的值传递给它。

是否有一种“干净”的说法:“是的,我实际上是想依靠JS返回undefined来访问对象上不存在的属性”? Esp。因为那是编写类型防护的全部要点。

2 个答案:

答案 0 :(得分:1)

我不知道在user-defined type guard实现中是否会出现诸如“不安全”之类的东西,因为通常这种类型保护的重点是允许编译器通过内置的控制流缩小来缩小通常无法执行的值。我当然会理解在这样的实现中暂停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';
}

无论如何,我希望这能使您更接近目标。祝你好运!

答案 1 :(得分:1)

另一种好的策略是将Partialas一起使用。

interface RegularForm {
    regular: number;
}

interface FancyForm extends RegularForm {
    fancy: string;
}

const isFancyForm = (instance: RegularForm): instance is FancyForm =>
    (instance as Partial<FancyForm>).fancy !== undefined;