typescript 缩小字符串到字符串文字联合

mitkmikd  于 2023-03-31  发布在  TypeScript
关注(0)|答案(4)|浏览(132)

我想把一个字符串缩小到一个字符串的字面联合。换句话说,我想检查这个字符串是否是我的字面联合的可能值之一,这样就可以工作了(如果运算符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,其中每个可能的值对应一个case,但是如果lit的允许值发生变化,则可能会导致细微的错误。

xnifntxz

xnifntxz1#

如果你像我一样讨厌转换病例:
由于TypeScript3.4- constAssert,因此也可以从字符串数组^_^中生成union类型

const lits = <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);
}
vhipe2zx

vhipe2zx2#

您可以使用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;
}
az31mfrm

az31mfrm3#

这是我对类型保护和关闭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;
}
brccelvz

brccelvz4#

您也可以使用zod枚举来执行此操作:

import zod from 'zod'

const ColorMode = zod.enum(['light', 'dark', 'system'] as const)

let _mode = 'light' // type is string
let mode = ColorMode.parse(_mode) // type is "light" | "dark" | "system"

_mode = 'twilight'
mode = ColorMode.parse(_mode) // throws an error, not a valid value

您还可以在需要时从zod模式中提取类型:

type ColorMode = zod.infer<typeof ColorMode>

我发现这样的验证库是解析、验证和限制类型的变量/数据的最简单、最健壮的方法,否则我必须手动编写容易出错的类型保护/ predicate 。

相关问题