我可以写一个类型后卫,抛出异常而不是返回布尔值吗?

时间:2018-04-06 05:30:03

标签: typescript nullable assertions

我有一个在多个函数中使用相同类型保护的类;像这样的东西:

function validData(d: Data | null): d is Data {
    return d !== null;
}

class C {
    data: Data | null;

    public doA() {
        if (!validData(this.data))
            throw new Error("Invalid data");
        /* … */
    }

    public doB() {
        if (!validData(this.data))
            throw new Error("Invalid data");
        /* … */
    }
}

我可以重构此代码以将错误移动到类型保护中吗?像这样:

function assertData(d: Data | null): ??? {
    if (d === null)
        throw new Error("Invalid data");
}

......我可以这样使用:

class C {
    data: Data | null;

    public doA() {
        assertData(this.data);
        /* … */
    }

    public doB() {
        assertData(this.data);
        /* … */
    }
}

目前我正在使用以下解决方法:

function must(d: Data | null): Data {
    if (d === null)
        throw new Error("Invalid data");
    return d;
}

...但这迫使我将this.data中的must()的每次访问权限包裹起来。

4 个答案:

答案 0 :(得分:2)

不幸的是,类型保护的当前语法需要if语句才能使用它们。所以这个工作

type Data = { foo: string };
function assertData(d: Data | null): d is Data {
    if (d == null)
        throw new Error("Invalid data");
    return true;
}
// Use
let bar: Data | null = null;
if (assertData(bar)) {
    bar.foo // inferred to be Data
}

但是没有办法让这个工作:

let bar: Data | null = null;
assertData(bar);
bar.foo // bar will still be Data | null

答案 1 :(得分:0)

不完全符合您的要求,但您可以添加is annotation以使用具有if条件的函数:

type Data = string;
function assertData(d: Data | null): d is Data {
  if (d == null)
    throw new Error("Invalid data");
  return true;
}
// Use
let foo: Data | null = null;
if (assertData(foo)) {
  foo.toUpperCase(); // inferred to be Data
}

答案 2 :(得分:0)

正如其他人已经指出的那样,没有通过类型防护来实现此目标的语法方法。我认为,解决TypeScript开发人员想要解决的问题的方法是使用新的变量或缩小类型的重新分配:

function assertData(d: Data | null): Data {
  if (d === null)
    throw new Error("Invalid data");
  return d;
}

class C {
  data: Data | null;

  public doA() {
    const data = assertData(this.data);
    /* do what you want with data instead of this.data */
  }
}

或者,您可以使用this.data进行重新分配,这应该足以缩小后续代码中的类型:

class C {
  data: Data | null;

  public doA() {
    this.data = assertData(this.data);
    /* … */
  }
}

这还具有比类型防护更安全的优势,因为类型防护使您可以“撒谎”变量的类型,而在这里是不可能的。

答案 3 :(得分:0)

您可以这样做,这是最方便(当前可能)的方式。

function validData(d: Data | null): d is Data {
  if (d === null) {
    throw new Error("Invalid data");
  }

  return d !== null;
}

class C {
  data: Data | null;

  public doA() {
    if (!validData(this.data)) return;

    /* typeof this.data equals Data now */
    /* … */
  }

  public doB() {
    if (!validData(this.data)) return;

    /* typeof this.data equals Data now */
    /* … */
  }
}