如何检查字符串是否包含在字符串文字类型中而不使用类型断言?



我经常使用像这样的字符串文字类型:

const supportedLanguageTags = [
"de-at",
"de-ch",
"de-de",
"en-gb",
"en-us",
] as const;
type LanguageTag = typeof supportedLanguageTags[number];
function isSupportedLanguageTag(tag: string): tag is LanguageTag {
return (supportedLanguageTags as unknown as string[]).includes(tag);
}

这里我使用一个数组来定义类型,但也能够检查是否随机字符串包含在文字类型中。

然而,我真的不喜欢类型断言。你有什么建议可以摆脱它吗?

一个解决方案是使用对象而不是数组,如下所示:

const supportedLanguageTags = {
"de-at": 1,
"de-ch": 1,
"de-de": 1,
"en-gb": 1,
"en-us": 1,
};
type LanguageTag = keyof typeof supportedLanguageTags;
function isSupportedLanguageTag(tag: string): tag is LanguageTag {
return tag in supportedLanguageTags;
}

但是这里我不喜欢为每个对象属性定义一个随机值。

在一个类似问题的答案中提到,Array.prototype.includes()调用签名的TypeScript类型要求搜索值与数组元素的类型相同。如果数组元素的类型是string,那么您可以搜索任何string。但是,如果数组元素具有比string更窄的类型,例如字符串文字类型的联合,那么您将无法搜索任意的string。这种限制对于类型安全来说并不是必需的;您应该能够安全地搜索比元素类型更宽的内容。

问题microsoft/TypeScript#26255被打开来请求这里的支持,但它被关闭为microsoft/TypeScript#14520的副本,这是关于一种表示超类型约束的方法。TypeScript只有extends,其中U extends T意味着U必须是T的某个子类型。对于Array<T>,您实际上需要类似includes<U super T>(searchElement: U): boolean的东西,其中U super T意味着U必须是T的某个超类型。但是TypeScript没有这样的语法。你可以用条件类型来模拟它,比如includes<U extends (T extends U ? unknown : never)>(searchElement: U),但这相当复杂,目前还不是TypeScript类型的一部分。


可以在这里做的一件事是安全地将数组的类型扩大为readonly string[]readonly数组类型是string的超类型,不知道有像push()这样的突变方法。字符串字面值数组可以安全地扩展为readonly string[],因为您不会修改其内容,并且将任何字符串字面值类型的值读入期望string的东西总是更安全。

类型断言允许(安全的)扩展和(不安全的)缩小。但是,如果您想确保没有意外地进行不安全的窄化,可以放弃类型断言,而是注释一个新变量。类型注释只支持安全加宽。

这意味着你可以这样写:

function isSupportedLanguageTag(tag: string): tag is LanguageTag {
const s: readonly string[] = supportedLanguageTags; // okay
return s.includes(tag); // okay
}

还有类型安全。同样,s变量并不是真正必要的,如果您更看重简洁而不是安全保证,那么类型断言将把它减少到更简洁的return (supportedLanguageTage as readonly string[]).includes(tag);

Playground链接到代码

有一个已知的Array.prototype.includes问题,但我找不到链接。为了欺骗TypeScript,你可以重载你的函数。由于重载工作是可变的,TS将允许您使用tag is LanguageTag返回类型:

const supportedLanguageTags = [
"de-at",
"de-ch",
"de-de",
"en-gb",
"en-us",
] as const;
type LanguageTag = typeof supportedLanguageTags[number];
function isSupported(tag: string): tag is LanguageTag
function isSupported(tag: any) {
return supportedLanguageTags.includes(tag)
}

isSupported('hello') // ok
isSupported(42) // error

游乐场

是的,我已经使用了any,但是你不能用数字呼叫isSupported,只有string

相关内容

  • 没有找到相关文章

最新更新