TypeScript 的一个函数写成如下:
function propKeyMap(propKey:string):string {
//TODO
}
Run Code Online (Sandbox Code Playgroud)
在propKey不能为 ""(空字符串)。我们可以写一个不包含空字符串的类型吗?
Gro*_*hni 14
从 TypeScript 4.1 开始,您可以使用模板文字类型来实现此目的。但是,它有点冗长,因为您必须定义长度为 1 的所有可能字符串的联合类型:
type Character = 'a' | 'b' | 'c' | ...;
type NonEmptyString = `${Character}${string}`;
Run Code Online (Sandbox Code Playgroud)
还有另一个使用条件类型和泛型的解决方案(4.1 之前):
type NonEmptyString<T extends string> = '' extends T ? never : T;
function propKeyMap<T extends string>(propKey: NonEmptyString<T>): string {
//TODO
}
Run Code Online (Sandbox Code Playgroud)
此解决方案对于函数很有用,因为您不必在调用它时显式指定泛型。但是,当在其他地方使用它时,您必须定义泛型,然后使其与用于泛型的类型相同:
let x: NonEmptyString<'foo'>; // This is of type 'foo'
let y: NonEmptyString<string>; //This is of type never
Run Code Online (Sandbox Code Playgroud)
同样,不可能将字符串传递给函数(这是正确的,因为字符串包含空字符串)。但这一事实,再加上无法将变量键入为 NonEmptyString,意味着这仅对使用字符串文字而不是变量直接调用的函数有用。
propKeyMap('foo'); // Compiles (as expected)
propKeyMap(''); // Errors (as expected)
const x: 'foo' = 'foo';
propKeyMap(x); // Compiles (as expected)
const y: string = 'foo';
propKeyMap(y); // Errors (unexpected)
Run Code Online (Sandbox Code Playgroud)
对于可以接受此限制的情况,我建议使用此解决方案,因为它不包括定义第一个解决方案所需的繁琐的联合类型。
jca*_*alz 11
不,你不能这样做。只需propKey在运行时检查该值,如果为空则抛出错误。
TypeScript 当前(从 v2.5 开始)缺少减法 types,因此无法告诉编译器类型是 astring但不是""。但是,有一些解决方法。
您可以使用品牌化(参见Microsoft/TypeScript #4895 中的讨论)来创建 的子类型string,然后尝试自己强制实施非空约束,因为 TypeScript 不能。例如:
type NonEmptyString = string & { __brand: 'NonEmptyString' };
Run Code Online (Sandbox Code Playgroud)
现在,您不能只string为NonEmptyString变量赋值:
const nope: NonEmptyString = 'hey'; // can't assign directly
Run Code Online (Sandbox Code Playgroud)
但是您可以创建一个接受 astring并返回 a的函数NonEmptyString:
function nonEmptyString(str: ""): never;
function nonEmptyString(str: string): NonEmptyString;
function nonEmptyString(str: string): NonEmptyString {
if (str === '')
throw new TypeError('empty string passed to nonEmptyString()');
return str as NonEmptyString;
}
Run Code Online (Sandbox Code Playgroud)
nonEmptyString()如果传入一个空的函数,该函数将在运行时炸毁string,所以只要你只NonEmptyString用这个函数构造对象,你就是安全的。此外,如果 TypeScript 知道您传入了一个空字符串,则返回的对象将属于类型never(本质上意味着它不应该发生)。所以它可以做一点编译时防范空字符串:
const okay = nonEmptyString('hey');
okay.charAt(0); // still a string
const oops = nonEmptyString(''); // will blow up at runtime
oops.charAt(0); // TypeScript knows that this is an error
Run Code Online (Sandbox Code Playgroud)
但这真的只是一点编译时的保护,因为很多时候 TypeScript 没有意识到 astring是空的:
const empty: string = ""; // you've hidden from TS the fact that empty is ""
const bigOops = nonEmptyString(empty); // will blow up at runtime
bigOops.charAt(0); // TypeScript doesn't realize that it will blow up
Run Code Online (Sandbox Code Playgroud)
不过,总比没有好……或者可能是。最重要的是,无论如何,您可能需要使用运行时检查空字符串进行编译时断言。
即使打字稿能表达NonEmptyString本身,就像这样string - "",编译器可能不会在大多数情况下,足够聪明,演绎出一个字符串操作的结果是或不是NonEmptyString。我的意思是,我们知道两个NonEmptyStrings的串联应该有至少两个的长度,但我怀疑 TypeScript 会:
declare let x: NonEmptyString;
declare let y: NonEmptyString;
const secondCharBad: NonEmptyString = (x + y).charAt(1); // won't work
const secondCharGood = nonEmptyString((x + y).charAt(1)); // okay
Run Code Online (Sandbox Code Playgroud)
那是因为您要求的类型接近向下倾斜到依赖类型的最顶端,这对开发人员的表现力很有帮助,但对编译器的可判定性不利。对于非空字符串,可能可以在类型级别上做一些合理的事情,但一般来说,您仍然会发现自己需要帮助编译器决定字符串何时实际上是非空的。
希望有帮助。祝你好运!
| 归档时间: |
|
| 查看次数: |
2643 次 |
| 最近记录: |