打字稿可以推断出一个论点已经通过验证吗?

时间:2019-12-25 03:24:51

标签: javascript typescript validation visual-studio-code

我仍在学习打字稿和Javascript,因此,如果我缺少某些内容,请原谅。

问题如下:

当前,如果调用 this.defined(email)时电子邮件未定义,VSCode不会推断出我抛出错误。这是由于 validateEmail()接受 string?而无法编译,因为它认为它仍然可以接收未定义的值(至少是我的推断)。有没有办法告诉编译器没问题?还是我错过了什么?

我想从其他类中调用Validate.validateEmail(),这些类中也会出现问题。

Validate.ts

export default class Validate {
    static validateEmail(email?: string) {
        // TODO: Test this regex properly.
        const emailRegex = new RegExp('^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$')

        this.defined(email);
        this.notEmpty(email);

        if (!emailRegex.test(email)) {
            throw new SyntaxError("The email entered is not valid");
        }
    }

    static defined(text?: string) {
        if (!text) {
            throw new SyntaxError("The text recieved was not defined.");
        }
    }

    static notEmpty(text: string) {
        if (text.length < 1) {
            throw new SyntaxError("The text entered is empty.");
        }
    }
}

3 个答案:

答案 0 :(得分:1)

您可以使用type guard。类型防护声明诸如此类的特定变量的类型:

export default class Validate {
  public static validateEmail(email?: string) {
    const emailRegex = new RegExp(
      '^(([^<>()[]\\.,;:s@"]+(.[^<>()[]\\.,;:s@"]+)*)|(".+"))@(([[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}])|(([a-zA-Z-0-9]+.)+[a-zA-Z]{2,}))$'
    );

    if (!Validate.defined(email)) {
      throw new SyntaxError('The text recieved was not defined.');
    }

    if (!emailRegex.test(email)) {
      throw new SyntaxError('The email entered is not valid');
    }
  }

  public static defined(text?: string): text is string {
    return !!text;
  }

  public static notEmpty(text: string) {
    if (text.length < 1) {
      throw new SyntaxError('The text entered is empty.');
    }
  }
}

不利之处在于,您仍然需要一个if语句,并且throw表达式在函数之外。

或者您可以执行类似的操作,从而避免了这些问题,但是需要您将email重新分配给函数的结果。

export default class Validate {
  public static validateEmail(email?: string) {
    const emailRegex = new RegExp(
      '^(([^<>()[]\\.,;:s@"]+(.[^<>()[]\\.,;:s@"]+)*)|(".+"))@(([[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}])|(([a-zA-Z-0-9]+.)+[a-zA-Z]{2,}))$'
    );

    email = Validate.defined(email);

    if (!emailRegex.test(email)) {
      throw new SyntaxError('The email entered is not valid');
    }
  }

  public static defined(text?: string): string {
    if (!text) {
      throw new SyntaxError('The text recieved was not defined.');
    }
    return text;
  }

  public static notEmpty(text: string) {
    if (text.length < 1) {
      throw new SyntaxError('The text entered is empty.');
    }
  }
}

答案 1 :(得分:1)

编译器有时可以在代码中的某些点识别出变量的类型比其注释或推断的类型窄analyzing the control flow。在以下代码块中,编译器理解,如果控制流到达email调用,则undefined不能为this.notEmpty(),因此没有错误:

if (!email) {
    throw new SyntaxError("The text received was not defined.");
}

this.notEmpty(email); // okay

但是,正如您所发现的,仅重构代码以使检查在其他函数中发生是行不通的。在执行控制流分析时,编译器通常不会将控制流纳入函数和方法中。这是tradeoff (see Microsoft/TypeScript#9998 in GitHub for more info):对于编译器来说,通过分析所有可能的函数调用中可能的控制流路径来模拟在所有可能的输入上运行的程序是不可行的,因此它必须在某处使用启发式方法。在这种情况下,启发式方法通常是“假定函数调用对变量类型没有影响”。因此,对this.defined(email)的调用不会对编译器看到的email的类型产生影响,因此会抱怨this.notEmpty(email)


幸运的是,TypeScript 3.7 introduced "assertion functions";您可以为函数提供特殊的返回类型,它告诉编译器传递给函数的变量将被函数缩小,并将其用作控制流分析的一部分。尽管编译器本身不会推断出此类函数签名,但至少现在您可以手动注释defined()断言其参数:

static defined(text?: string): asserts text {
    if (!text) {
        throw new SyntaxError("The text recieved was not defined.");
    }
}

defined()的返回类型为asserts text,这意味着如果text返回,defined()将被验证为真。这可以解决您的原始示例:

this.defined(email);
this.notEmpty(email); // okay now!

看起来不错。好的,希望能有所帮助;祝你好运!

Playground link to code

答案 2 :(得分:0)

未编译代码的原因是notnotty需要接受可为空的字符串。这是因为在调用站点,您正在传递一个可为空的字符串作为参数。以下代码解决了代码问题。

export default class Validate {
    static validateEmail(email?: string) {
        // TODO: Test this regex properly.
        const emailRegex = new RegExp('^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$')

        this.defined(email);
        this.notEmpty(email);

        if (email && !emailRegex.test(email)) {
            throw new SyntaxError("The email entered is not valid");
        }
    }

    static defined(text?: string) {
        if (!text) {
            throw new SyntaxError("The text recieved was not defined.");
        }
    }

    static notEmpty(text?: string) {
        if (text && text.length < 1) {
            throw new SyntaxError("The text entered is empty.");
        }
    }
}