例如我有一个用户类型:
type User = {
name: string;
city: string;
email: Email;
password: Password;
}
Run Code Online (Sandbox Code Playgroud)
我可以在电子邮件中使用string,但有一些限制,这就是我想在此处输入电子邮件的原因。限制是:
它必须具有某种形状。仅允许使用字符 az 和 2 个特殊字符.以及 @。例如,dave.morrison@gmail.com 是有效的形状,dave-morrison@gmail.com 无效。
它的长度必须有限。
就像是:
type Email = /pattern here/
Run Code Online (Sandbox Code Playgroud)
可以制作这种类型吗?
正确的答案可能是使用品牌的名义类型,如 @LionelRowe 的答案;除非您正在处理字符串文字类型,以便这些电子邮件地址字符串在代码中被硬编码为字符串文字,否则编译器无法强制执行此类约束。编译器只会看到string.
即使编译器知道您指定的确切字符串文字(例如 )const str = "foo@bar.com",TypeScript 4.0 也无法验证这样的字符串。
令人惊奇的是,它不会超出TypeScript 4.1 的能力,TypeScript 4.1 将引入模板文字类型(如在microsoft/TypeScript#40336中实现的)...它实际上可以对字符串文字进行操作。
不幸的是,据我所知,这将处于TypeScript 能力的边缘。为了让编译器验证字符串的长度不超过(比如说)32 个字符,并且它必须仅包含字母字符 、"."和"@",您必须使用泛型和可能的递归条件类型(也适用于 TS4.5)。 1 在microsoft/TypeScript#40002中实现)。每一次你都会发现自己在避免各种递归或组合限制;对于只有 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
Run Code Online (Sandbox Code Playgroud)
该类型RestrictedToChars<T, A, Y, N>采用字符串文字T和允许的字符的并集A,并且计算结果为:Y如果T仅包含 中的字符A,或者N如果T包含不在中的任何字符A。
type SplitChars<T extends string> = string extends T ? string[] :
T extends `${infer F1}${infer F2}${infer F3}${infer F4}${infer F5}${infer F6}${infer R}` ? [F1, F2, F3, F4, F5, F6, ...SplitChars<R>] :
T extends `${infer F1}${infer F2}${infer F3}${infer R}` ? [F1, F2, F3, ...SplitChars<R>] :
T extends `${infer F1}${infer R}` ? [F1, ...SplitChars<R>] :
[]
type CheckMaxLength<T extends string, L extends number, Y = T, N = never> =
SplitChars<T>[L] extends undefined ? Y : N;
Run Code Online (Sandbox Code Playgroud)
该类型CheckMaxLength<T, L, Y, N>采用字符串文字T和最大数字长度L,计算结果为:YifT至多L字符长,或者NifT超过L字符长。
然后,您可以Email<T>在字符串文字类型上定义为通用验证器T,并User<T>以相同的方式使通用:
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>;
}
Run Code Online (Sandbox Code Playgroud)
最后你可以看到编译器进行验证:
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"]'
Run Code Online (Sandbox Code Playgroud)
极其可怕!再次强调,我不建议您使用这个。但当 TS4.1 发布时,技术上可以让编译器以这种方式验证字符串。
| 归档时间: |
|
| 查看次数: |
2097 次 |
| 最近记录: |