鉴于enum看起来像这样:
export enum UsedProduct {
Yes = 'yes',
No = 'no',
Unknown = 'unknown',
}
Run Code Online (Sandbox Code Playgroud)
我想写一个函数,它接受一组字符串文字并返回一个实例UsedProduct.到目前为止,我写了一个这样的函数:
export function parseUsedProduct(usedProdStr: 'yes' | 'no' | 'unknown'): UsedProduct {
switch (usedProdStr) {
case 'yes':
return UsedProduct.Yes;
case 'no':
return UsedProduct.No;
case 'unknown':
return UsedProduct.Unknown;
default:
return unknownUsedProductValue(usedProdStr);
}
}
function unknownUsedProductValue(usedProdStr: never): UsedProduct {
throw new Error(`Unhandled UsedProduct value found ${usedProdStr}`);
}
Run Code Online (Sandbox Code Playgroud)
这个实现并不是很好,因为我必须重新定义枚举的可能值.如何重写此功能以便我不必定义'yes' | 'no' | 'unknown'?
TypeScript不会让你这么容易,所以答案不是单行.
一个enum像值UsedProduct.Yes仅仅是一个字符串或数字运行时的文字(在这种情况下,字符串"yes"),但在编译时它被当作一个亚型的字符串或数字的字面.所以,UsedProduct.Yes extends "yes"是的.不幸的是,鉴于类型UsedProduct.Yes,没有编程方式将类型扩展为"yes"...或者,给定类型UsedProduct,没有编程方法来扩展它"yes" | "no" | "unknown".该语言缺少一些您需要执行此操作的功能.
有是一种方法,使一个函数签名一个功能类似parseUsedProduct,但它使用泛型和有条件的类型来实现这一点:
type Not<T> = [T] extends [never] ? unknown : never
type Extractable<T, U> = Not<U extends any ? Not<T extends U ? unknown : never> : never>
declare function asEnum<E extends Record<keyof E, string | number>, K extends string | number>(
e: E, k: K & Extractable<E[keyof E], K>
): Extract<E[keyof E], K>
const yes = asEnum(UsedProduct, "yes"); // UsedProduct.yes
const no = asEnum(UsedProduct, "no"); // UsedProduct.no
const unknown = asEnum(UsedProduct, "unknown"); // UsedProduct.unknown
const yesOrNo = asEnum(UsedProduct,
Math.random()<0.5 ? "yes" : "no"); // UsedProduct.yes | UsedProduct.no
const unacceptable = asEnum(UsedProduct, "oops"); // error
Run Code Online (Sandbox Code Playgroud)
基本上它采用枚举对象类型E和字符串或数字类型K,并尝试提取该E扩展的属性值K.如果没有Eextend的值K(或者如果K是其中一个部分与任何值不对应的union类型E),编译器将给出错误.可根据要求提供具体方法Not<>和Extractable<>工作方法.
至于函数的实现,您可能需要使用类型断言.就像是:
function asEnum<E extends Record<keyof E, string | number>, K extends string | number>(
e: E, k: K & Extractable<E[keyof E], K>
): Extract<E[keyof E], K> {
// runtime guard, shouldn't need it at compiler time
if (Object.values(e).indexOf(k) < 0)
throw new Error("Expected one of " + Object.values(e).join(", "));
return k as any; // assertion
}
Run Code Online (Sandbox Code Playgroud)
这应该工作.在您的具体情况下,我们可以硬编码UsedProduct:
type Not<T> = [T] extends [never] ? unknown : never
type Extractable<T, U> = Not<U extends any ? Not<T extends U ? unknown : never> : never>
function parseUsedProduct<K extends string | number>(
k: K & Extractable<UsedProduct, K>
): Extract<UsedProduct, K> {
if (Object.values(UsedProduct).indexOf(k) < 0)
throw new Error("Expected one of " + Object.values(UsedProduct).join(", "));
return k as any;
}
const yes = parseUsedProduct("yes"); // UsedProduct.yes
const unacceptable = parseUsedProduct("oops"); // error
Run Code Online (Sandbox Code Playgroud)
希望有所帮助.祝好运!
| 归档时间: |
|
| 查看次数: |
3127 次 |
| 最近记录: |