繁体   English   中英

打字稿可以推断出一个参数已经过验证吗?

[英]Can typescript infer that an argument has been validated?

我还在学习 Typescript 和 Javascript,所以如果我遗漏了什么,请原谅。

问题如下:

当前,如果在调用this.defined(email)时电子邮件未定义,VSCode 不会推断我抛出错误。 这是因为validateEmail()接受一个字符串? 并且不会编译,因为它认为它仍然可以收到一个未定义的值(至少是我的推论)。 有没有办法告诉编译器这没问题? 或者我错过了什么?

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

验证.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.");
        }
    }
}

您可以使用类型保护 类型保护声明特定变量的类型,如下所示:

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重新分配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.');
    }
  }
}

通过分析控制流,编译器有时可以识别出变量的类型比代码中某些点的注释或推断类型窄。 在下面的代码块中,编译器知道如果控制流到达this.notEmpty()调用,则无法取消undefined email ,因此没有错误:

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

this.notEmpty(email); // okay

但是,正如您所发现的,简单地重构代码以在不同的函数中进行检查是行不通的。 在执行控制流分析时,编译器通常不会遵循控制流进入函数和方法。 这是一种权衡(有关更多信息,请参阅 GitHub 中的 Microsoft/TypeScript#9998) :编译器通过分析所有可能函数调用的可能控制流路径来模拟在所有可能输入上运行的程序是不可行的,因此它具有在某处使用启发式方法; 在这种情况下,启发式通常是“假设函数调用对变量类型没有影响”。 因此,调用this.defined(email)对编译器看到的email类型没有影响,因此它会抱怨this.notEmpty(email)


幸运的是, TypeScript 3.7 引入了“断言函数” 你可以给一个函数一个特殊的返回类型,它告诉编译器传递给函数的变量将被函数缩小,并且它将使用它作为其控制流分析的一部分。 虽然编译器本身不会推断此类函数签名,但至少现在您可以手动注释defined()对其参数的断言:

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

defined()的返回类型是asserts text ,这意味着如果defined()返回,则text将被验证为真。 这修复了您的原始示例:

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

看起来挺好的。 好的,希望有帮助; 祝你好运!

Playground 链接到代码

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

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.");
        }
    }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM