typescript - 在运行时检查字符串文字联合类型的有效性?

标签 typescript

我有一个简单的联合类型的字符串文字,需要检查它的有效性,因为 FFI 调用“普通”Javascript。有没有办法确保某个变量是任何这些文字字符串的实例在运行时?类似的东西

type MyStrings = "A" | "B" | "C";
MyStrings.isAssignable("A"); // true
MyStrings.isAssignable("D"); // false

最佳答案

从 Typescript 3.8.3 开始,没有明确的最佳实践。似乎有三种不依赖于外部库的解决方案。在所有情况下,您都需要将字符串存储在运行时可用的对象中(例如数组)。

对于这些例子,假设我们需要一个函数来在运行时验证一个字符串是否是任何规范的羊名,我们都知道它是 Capn FriskyMr. Snugs羊排。以下是 Typescript 编译器可以理解的三种方式。

1:类型断言(更简单)

摘下 Helm ,自己验证类型,然后使用断言。

const sheepNames = ['Capn Frisky', 'Mr. Snugs', 'Lambchop'] as const;
type SheepName = typeof sheepNames[number]; // "Capn Frisky" | "Mr. Snugs" | "Lambchop"

// This string will be read at runtime: the TS compiler can't know if it's a SheepName.
const unsafeJson = '"Capn Frisky"';

/**
 * Return a valid SheepName from a JSON-encoded string or throw.
 */
function parseSheepName(jsonString: string): SheepName {
    const maybeSheepName: unknown = JSON.parse(jsonString);
    // This if statement verifies that `maybeSheepName` is in `sheepNames` so
    // we can feel good about using a type assertion below.
    if (typeof maybeSheepName === 'string' && sheepNames.includes(maybeSheepName)) {
        return (maybeSheepName as SheepName); // type assertion satisfies compiler
    }
    throw new Error('That is not a sheep name.');
}

const definitelySheepName = parseSheepName(unsafeJson);

PRO:简单易懂。

缺点: 脆弱。 Typescript 只是相信您已经充分验证了 maybeSheepName。如果您不小心删除了支票,Typescript 将无法保护您免受自己的伤害。

2:自定义类型守卫(更可重用)

这是上述类型断言的一个更高级、更通用的版本,但它仍然只是一个类型断言。

const sheepNames = ['Capn Frisky', 'Mr. Snugs', 'Lambchop'] as const;
type SheepName = typeof sheepNames[number];

const unsafeJson = '"Capn Frisky"';

/**
 * Define a custom type guard to assert whether an unknown object is a SheepName.
 */
function isSheepName(maybeSheepName: unknown): maybeSheepName is SheepName {
    return typeof maybeSheepName === 'string' && sheepNames.includes(maybeSheepName);
}

/**
 * Return a valid SheepName from a JSON-encoded string or throw.
 */
function parseSheepName(jsonString: string): SheepName {
    const maybeSheepName: unknown = JSON.parse(jsonString);
    if (isSheepName(maybeSheepName)) {
        // Our custom type guard asserts that this is a SheepName so TS is happy.
        return (maybeSheepName as SheepName);
    }
    throw new Error('That is not a sheep name.');
}

const definitelySheepName = parseSheepName(unsafeJson);

PRO:可重用性更高,脆弱性略微降低,可以说更具可读性。

缺点:Typescript 仍然只是相信您的话。对于如此简单的事情,似乎有很多代码。

3:使用Array.find(最安全,推荐)

这不需要类型断言,以防您(像我一样)不相信自己。

const sheepNames = ['Capn Frisky', 'Mr. Snugs', 'Lambchop'] as const;
type SheepName = typeof sheepNames[number];

const unsafeJson = '"Capn Frisky"';

/**
 * Return a valid SheepName from a JSON-encoded string or throw.
 */
function parseSheepName(jsonString: string): SheepName {
    const maybeSheepName: unknown = JSON.parse(jsonString);
    const sheepName = sheepNames.find((validName) => validName === maybeSheepName);
    if (sheepName) {
        // `sheepName` comes from the list of `sheepNames` so the compiler is happy.
        return sheepName;
    }
    throw new Error('That is not a sheep name.');
}

const definitelySheepName = parseSheepName(unsafeJson);

PRO:不需要类型断言,编译器仍在进行所有验证。这对我很重要,所以我更喜欢这个解决方案。

缺点:看起来有点奇怪。更难优化性能。


就这样吧。您可以合理地选择这些策略中的任何一种,或者使用其他人推荐的第 3 方库。

Sticklers 会正确地指出这里使用数组是低效的。您可以通过将 sheepNames 数组转换为 O(1) 查找集来优化这些解决方案。如果您要处理数以千计的潜在绵羊名称(或其他名称),这是值得的。

关于typescript - 在运行时检查字符串文字联合类型的有效性?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36836011/

相关文章:

Angular2 authguards 执行异步函数失败

Typescript 路径在 Express 项目中不起作用

javascript - 服务中的 Angular 2 和 rxjs 主题未在组件上更新

typescript - 如何在 typescript 中显式定义具有不同元组类型的元组数组?

javascript - 如何传递具有动态引用angular2的元素?

typescript - 忽略来自 VS2017 中第三方 javascript 文件的 TS7027 警告?

html - Angular 2复选框双向数据绑定(bind)

javascript - 如何使用forwardRef()进行循环依赖?

node.js - TypeScript 外部 Node 模块有时会转换为 module.exports 和导出

javascript - 检测到但未使用的 VScode sourceMaps