Typescript 我可以创建比字符串更具体的类型吗?

Typescript 我可以创建比字符串更具体的类型吗?,typescript,Typescript,例如,我有一个用户类型: type User = { name: string; city: string; email: Email; password: Password; } 我可以在电子邮件中使用string,但我有一些限制,这就是为什么我想在这里键入电子邮件。限制包括: 它一定是某种形状的。只允许使用字符a-z和两个特殊字符,和@。例如,戴夫。morrison@gmail.com这是有效的形状,戴夫-morrison@gmail.com无效 它的长度必须有限 比如

例如,我有一个用户类型:

type User = {
  name: string;
  city: string;
  email: Email;
  password: Password;
}
我可以在电子邮件中使用
string
,但我有一些限制,这就是为什么我想在这里键入电子邮件。限制包括:

  • 它一定是某种形状的。只允许使用字符a-z和两个特殊字符,
    和@。例如,戴夫。morrison@gmail.com这是有效的形状,戴夫-morrison@gmail.com无效

  • 它的长度必须有限

  • 比如:

    type Email = /pattern here/
    

    是否可以创建此类型?

    唯一比字符串更具体的类型是特定字符串:

    不幸的是,你只能这么做。如果输入不受几个硬编码值的限制,则特定的Typescript类型并不用于指定有关一般用户输入的内容,而不是其类型。出于类似的原因,你不能做类似的事情

    type BigNum = number > 1000;
    

    唯一比字符串更具体的类型是特定字符串:

    不幸的是,你只能这么做。如果输入不受几个硬编码值的限制,则特定的Typescript类型并不用于指定有关一般用户输入的内容,而不是其类型。出于类似的原因,你不能做类似的事情

    type BigNum = number > 1000;
    

    正确的答案可能是像@lionelrowee那样使用品牌的一种类似于名义的类型;除非您正在处理这些电子邮件地址字符串,以便这些电子邮件地址字符串在代码中硬编码为字符串文本,否则编译器无法强制执行此类约束。编译器将只看到
    字符串

    即使编译器知道您指定的确切字符串文本,例如
    const str=”foo@bar.com“
    ,像这样验证字符串超出了TypeScript 4.0的能力


    令人惊奇的是,它不会超出TypeScript 4.1的能力,它将引入模板文字类型(如中实现的)。。。它实际上可以对字符串文本执行操作

    不幸的是,据我所知,它将处于TypeScript能力的边缘。为了让编译器验证字符串长度不超过(比如)32个字符,并且它必须只包含字母字符、
    ”和
    “@”
    ,您必须使用递归条件类型(也计划在中实现TS4.1)。在每一个转弯处,你都会发现自己在避免各种递归或组合限制;对于长度仅为10个字符左右的字符串,编写一些中断编译器的代码是很容易的。即使您设法不达到极限,编译器也会变得很慢。键入无效电子邮件并等待10秒钟,编译器才会显示错误。这是一个混乱,我不推荐它,至少在TS4.1中是这样

    但我太喜欢这些东西了,不能不做:

    type Lower = 'a' | 'b' | 'c' | 'd' | 'e' | 'f' | 'g' | 'h' | 'i' | 'j' | 'k' | 'l' | 'm' | 'n' | 'o' | 'p' | 'q' | 'r' | 's' | 't' | 'u' | 'v' | 'w' | 'x' | 'y' | 'z';
    type AllowedChars = Lower | '.' | '@'
    
    type RestrictedToChars<T extends string, A extends string, Y = T, N = never> =
      string extends T ? N :
      T extends `${infer F}${infer F}${infer F}${infer F}${infer F}${infer F}${infer R}` ?
      [F] extends [A] ? RestrictedToChars<R, A, Y, N> : N :
      T extends `${infer F}${infer F}${infer F}${infer R}` ?
      [F] extends [A] ? RestrictedToChars<R, A, Y, N> : N :
      T extends `${infer F}${infer R}` ?
      [F] extends [A] ? RestrictedToChars<R, A, Y, N> : N :
      Y
    
    类型
    CheckMaxLength
    采用字符串文字
    T
    和最大数字长度
    L
    ,如果
    T
    最多
    L
    个字符,则计算为:
    Y
    ,如果
    T
    超过
    L
    个字符,则计算为
    N

    然后,您可以将
    Email
    定义为字符串文本类型
    T
    上的通用验证器,并以相同的方式使
    User
    通用:

    type Email<T extends string> = T &
      CheckMaxLength<T, 32, RestrictedToChars<
        `${lowercase T}`, AllowedChars, T, ["Email can only contain alphabet or @ or ."]
      >, ["Email needs to be 32 characters or less"]>;
    
    type User<T extends string> = {
      name: string;
      email: Email<T>;
    }
    
    type Email=T&
    CheckMaxLength,[“电子邮件需要不超过32个字符”]>;
    类型用户={
    名称:字符串;
    电邮:电邮;
    }
    
    最后,您可以看到编译器执行验证:

    const user = <T extends string>(user: User<T>) => user;
    
    user({ name: "Dave Morrison", email: "Dave.Morrison@gmail.com" }); // okay
    user({ name: "Van Morrison", email: "Van-Morrison@gmail.com" }); // error!
    // ------------------------> ~~~~~
    // '["Email can only contain alphabet or @ or ."]'
    user({ name: "Jim Morrison", email: "Jim.Morrison.is.my.name.and.it.is.too.long@gmail.com" }); // error!
    // ------------------------> ~~~~~
    // '["Email needs to be 32 characters or less"]'
    
    constuser=(user:user)=>user;
    用户({姓名:“Dave Morrison”,电子邮件:“Dave。Morrison@gmail.com" }); // 可以
    用户({姓名:“Van Morrison”,电子邮件:“Van-Morrison@gmail.com" }); // 错误!
    // ------------------------> ~~~~~
    //“[”电子邮件只能包含字母或@或。“”
    用户({name:“Jim Morrison”,电子邮件:“Jim.Morrison.is.my.name.and.it.is.too。long@gmail.com" }); // 错误!
    // ------------------------> ~~~~~
    //“[“电子邮件必须少于32个字符”]
    
    非常可怕!同样,我不建议你使用这个。但是当TS4.1发布时,从技术上讲,编译器可以通过这种方式验证字符串


    正确的答案可能是使用@LionelRowee品牌的类似名义的类型;除非您正在处理这些电子邮件地址字符串,以便这些电子邮件地址字符串在代码中硬编码为字符串文本,否则编译器无法强制执行此类约束。编译器将只看到
    字符串

    即使编译器知道您指定的确切字符串文本,例如
    const str=”foo@bar.com“
    ,像这样验证字符串超出了TypeScript 4.0的能力


    令人惊奇的是,它不会超出TypeScript 4.1的能力,它将引入模板文字类型(如中实现的)。。。它实际上可以对字符串文本执行操作

    不幸的是,据我所知,它将处于TypeScript能力的边缘。为了让编译器验证字符串长度不超过(比如)32个字符,并且它必须只包含字母字符、
    ”和
    “@”
    ,您必须使用递归条件类型(也计划在中实现TS4.1)。在每一个转弯处,你都会发现自己在避免各种递归或组合限制;对于长度仅为10个字符左右的字符串,编写一些中断编译器的代码是很容易的。即使您设法不达到极限,编译器也会变得很慢。键入无效电子邮件并等待10秒钟,编译器才会显示错误。这是一个混乱,我不推荐它,至少在TS4.1中是这样

    但我太喜欢这些东西了,不能不做:

    type Lower = 'a' | 'b' | 'c' | 'd' | 'e' | 'f' | 'g' | 'h' | 'i' | 'j' | 'k' | 'l' | 'm' | 'n' | 'o' | 'p' | 'q' | 'r' | 's' | 't' | 'u' | 'v' | 'w' | 'x' | 'y' | 'z';
    type AllowedChars = Lower | '.' | '@'
    
    type RestrictedToChars<T extends string, A extends string, Y = T, N = never> =
      string extends T ? N :
      T extends `${infer F}${infer F}${infer F}${infer F}${infer F}${infer F}${infer R}` ?
      [F] extends [A] ? RestrictedToChars<R, A, Y, N> : N :
      T extends `${infer F}${infer F}${infer F}${infer R}` ?
      [F] extends [A] ? RestrictedToChars<R, A, Y, N> : N :
      T extends `${infer F}${infer R}` ?
      [F] extends [A] ? RestrictedToChars<R, A, Y, N> : N :
      Y
    
    类型
    CheckMaxLength
    采用字符串文字
    T
    和最大数字长度
    L
    ,如果
    T
    最多
    L
    个字符,则计算为:
    Y
    ,如果
    T
    大于
    L
    个字符,则计算为
    N
    type Email = string & { readonly email: unique symbol }
    
    type User = {
        name: string
        email: Email
    }
    
    // check with `is` return type
    const isValidEmail = (maybeEmail: string): maybeEmail is Email => {
        return maybeEmail.includes('@') && maybeEmail.length < 100
    }
    
    const aliceEmail = 'alice@email.com'
    
    // OK - runtime type safety
    if (isValidEmail(aliceEmail)) {
        const alice: User = {
            name: 'Alice',
            email: aliceEmail,
        }
    }
    
    // compiles with `as` (bypassing runtime safety)
    const bob: User = {
        name: 'Bob',
        email: 'bob@email.com' as Email,
    }
    
    // compile-time error
    const charlie: User = {
        name: 'Charlie',
        email: 'charlie@email.com',
    }