我有一个枚举:
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 类型)是类型级别的枚举的有效值?
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 代码链接