对常量字符串进行通用检查是否有效的枚举值

问题描述 投票:0回答:1

我有一个枚举:

enum MimeType {
  VideoMP4 = 'video/mp4',
  VideoQuicktime = 'video/quicktime',
}

我希望能够从函数的字符串输入推断枚举值:

function createSomething<const V extends `${string}:${MimeType}`>(value: V): V extends `${string}:${infer M}` 
  ? M extends MimeType 
    ? Something<M>
    : never
  : never;

哪里

type Something<M extends MimeType> = ...

在上面的

createSomething
中,我总是得到
never
。三元组中似乎失败的部分是
M extends MimeType
。例如:

type Test1 = 'video/mp4' extends MimeType ? true : false; // false

但是:

type Test2 = 'video/mp4' extends MimeType[keyof MimeType] ? true : false; // true

但这并不与

Something
兼容。

我也尝试过:

function createSomething<M extends MimeType, const V extends `${string}:${M}`>(value: V): Something<V>;

但是,即使传入特定的

M
,这也会将
MimeType
概括为
V
,可能是因为它仍然无法将 M 推断为特定的枚举成员。

有没有办法断言字符串(const 类型)是类型级别的枚举的有效值?

typescript enums
1个回答
0
投票
TypeScript 中的

Enum 类型被视为其值的 nominal subtypes。也就是说,

MimeType.VideoMP4
可分配给字符串文字类型'video/mp4'
,但至关重要的是
反之亦然。枚举值对于您的代码来说应该是不透明的;使用枚举的目的之一是要求您实际使用枚举。所以 const okay: MimeType = MimeType.VideoMP4;

无论结果是什么,
总是正确的,而

const bad: MimeType = "video/mp4"

即使该值位于枚举中,
始终是一个错误。它是该语言中为数不多的以名义方式处理类型的地方之一。如果您不喜欢这种行为,您实际上可能根本不需要枚举。

正如文档中提到的

,您可能只需要一个 const-asserted

 对象:
const MimeType = { VideoMP4: 'video/mp4', VideoQuicktime: 'video/quicktime', } as const; type MimeType = typeof MimeType[keyof typeof MimeType];

这使您的代码按原样工作:

const x = createSomething("abc:video/mp4"); // ^? const x: Something<"video/mp4"> const y = createSomething("def:video/quicktime"); // ^? const y: Something<MimeType.VideoQuicktime> const z = createSomething("ghi:video/oops"); // error

如果您坚持使用枚举,您仍然可以通过在 
createSomething()

子句中使用

模板文字类型
extends 约束来挽救
infer
:
declare function createSomething< const V extends `${string}:${MimeType}`>(value: V): V extends `${string}:${infer M}` ? M extends `${infer MM extends MimeType}` ? Something<MM> : never : never;

这会提示编译器将 
MM

推断为

MimeType
的子类型,当序列化为字符串时,它是
M
。它也有效:
const x = createSomething("abc:video/mp4");
//    ^? const x: Something<MimeType.VideoMP4>
const y = createSomething("def:video/quicktime");
//    ^? const y: Something<MimeType.VideoQuicktime>
const z = createSomething("ghi:video/oops"); // error

Playground 代码链接

© www.soinside.com 2019 - 2024. All rights reserved.