【问题标题】:Narrowing string to string literal union将字符串缩小到字符串文字并集
【发布时间】:2026-02-06 23:25:02
【问题描述】:

我想将字符串缩小为字符串文字联合。换句话说,我想检查字符串是否是我的文字联合的可能值之一,以便这将起作用(如果运算符 couldbe 存在)。

type lit = "A" | "B" | "C";
let uni: lit;
let str = "B";
if(str couldbe lit){
    uni = str;
} else {
    doSomething(str);
}

我怎样才能做到这一点?

我尝试使用if (str instanceof lit),但这似乎不起作用。使用keyof 遍历字符串联合也不起作用,因为允许的值本身不是键。

一种方法是使用switch 对每个可能的值使用一种情况,但如果lits 允许的值发生变化,这可能会导致细微的错误。

【问题讨论】:

  • lit 类型在运行时不存在,因此您不能这样使用它。也许改用枚举?
  • 关于switch语句注释,见answer
  • @NitzanTomer 这实际上是一个非常好的主意,看起来更清晰,更容易理解。

标签: string typescript types literals unions


【解决方案1】:

您可以使用User-Defined Type Guards

type lit = "A" | "B" | "C";
let uni: lit;
let str = "B";

function isLit(str: string): str is lit {
    return str == "A" || str == "B" || str == "C";
}
function doSomething(str: string) {

}

if (isLit(str)) {
    uni = str;
}
else {
    doSomething(str);
}

添加:

为避免重复编辑,class 可用于编译时和运行时。现在您只需编辑一处即可。

class Lit {
    constructor(public A = 0, public B = 0, public C = 0) {}
}
type lit = keyof Lit;
let uni: lit;

function isLit(str: string): str is lit {
    let lit = new Lit();
    return (str in lit) ? true : false;
}

【讨论】:

  • 嗯,这比 switch 语句好一点,但它仍然存在当lits 允许的值更改时可能忘记更新类型保护的问题。
  • 我为我的答案添加了另一个解决方案。
  • 如果绝对必须使用文字联合,我非常喜欢您的第二种解决方案。不过,似乎更好的方法是切换到enums。
【解决方案2】:

这是我对类型保护和关闭strictNullChecks 问题的看法(这是对项目的限制;如果此选项为true,TS 将要求对switch/case 进行详尽说明)。

const _notLit: never = maybeLit; 行保证当您更改lit 类型时,您还需要更新switch/case

此解决方案的缺点是,随着联合类型 lit 的增长,它会变得非常冗长。

type lit = "A" | "B" | "C";

function isLit(str: string): str is lit {
  const maybeLit = str as lit;
  switch (maybeLit) {
    case "A":
    case "B":
    case "C":
      return true;
  }

  // assure exhaustiveness of the switch/case
  const _notLit: never = maybeLit;

  return false;
}

如果可能此任务更适合 enum 或者如果您需要 type 并且不介意创建底层枚举以进行检查,您可以创建类似这样的类型保护:

enum litEnum {
  "A",
  "B",
  "C",
}
type lit = keyof typeof litEnum;

function isLit(str: string): str is lit {
  return litEnum[str] !== undefined;
}

【讨论】:

    【解决方案3】:

    如果您像我一样讨厌开关盒:
    因为TypeScript 3.4 – const assertions 也可以从你的字符串数组中生成联合类型^_^

    const list = <const>["A", "B", "C"];
    type Lit = typeof list[number]; // "A" | "B" | "C"
    
    function isLit(str: string): str is Lit {
      return !!lits.find((lit) => str === lit);
    }
    

    【讨论】: