دریافت کلید enum با رشته مقدار (نگاشت معکوس) در TypeScript
من یک شمارش دارم:
export enum ApiMessages {
logged_ok = 'Logged OK',
register_ok = 'Register OK'
}
من یک تابع دارم که enum را به عنوان پارامتر دارد:
export function responseOK(message: ApiMessages, result ?: any): ApiResponse {
return {
"status": "ok",
"code": 200,
"messageId": ApiMessages[message], <-- KO TS7015
"message": message,
"result": result
};
}
من تابع را اینطور فراخوانی میکنم:
responseOK(ApiMessages.logged_ok, {user: userRes})
من در تلاش هستم تا کلید انوم و مقدار رشتهای انوم را به پاسخ بازگردانم اما خطای TS میگیرم:
TS7015: عنصر بهطور ضمنی نوع 'any' دارد زیرا عبارت شاخص از نوع 'number' نیست.
من تنظیمات سختگیرانهای برای TypeScript دارم. افزودن suppressImplicitAnyIndexErrors گزینهای نیست.
نسخه TypeScript: 2.9.2
پاسخ شماره ۱
همانطور که در دفترچه راهنما توضیح داده شده است:
به خاطر داشته باشید که اعضای شمارنده رشتهای اصلاً نگاشت معکوس تولید نمیکنند.
این به این معناست که در مورد شما هیچ نگاشت معکوس سادهای وجود ندارد.
راهحل موقت: بهدست آوردن نگاشت معکوس برای اعضای شمارش رشتهای
برای به دست آوردن کلید یک عضو Enum با مقدار آن، باید از کلیدهای Enum عبور کنید و مقدار مرتبط را با مقدار هدف خود مقایسه کنید.
function getEnumKeyByEnumValue(myEnum, enumValue) {
let keys = Object.keys(myEnum).filter(x => myEnum[x] == enumValue);
return keys.length > 0 ? keys[0] : null;
}
شما میتوانید این را به صورت دقیقتر به شکل زیر تایپ کنید (توجه داشته باشید که میتوانیم enum خود را به عنوان یک نوع قابل ایندکس با کلید و مقدار هر دو رشته در اینجا تفسیر کنیم):
function getEnumKeyByEnumValue<T extends {[index:string]:string}>(myEnum:T, enumValue:string):keyof T|null {
let keys = Object.keys(myEnum).filter(x => myEnum[x] == enumValue);
return keys.length > 0 ? keys[0] : null;
}
کدی نمونه در ادامه آمده است. شما همچنین میتوانید آن را در عمل در محیط بازی TypeScript ببینید
enum ApiMessages {
logged_ok = 'Logged OK',
register_ok = 'Register OK'
}
let exampleValue = ApiMessages.logged_ok;
let exampleKey = getEnumKeyByEnumValue(ApiMessages, exampleValue);
alert(`The value '${exampleValue}' has the key '${exampleKey}'`)
function getEnumKeyByEnumValue<T extends {[index:string]:string}>(myEnum:T, enumValue:string):keyof T|null {
let keys = Object.keys(myEnum).filter(x => myEnum[x] == enumValue);
return keys.length > 0 ? keys[0] : null;
}
با اضافه کردن این به responseOK() شما در نهایت به این نتیجه میرسید:
function responseOK(message: ApiMessages, result ?: any) {
return {
"status": "ok",
"code": 200,
"messageId": getEnumKeyByEnumValue(ApiMessages, message),
"message": message,
"result": result
};
}
پاسخ شماره ۲
اگر یک رشته (یا هر مقدار دیگری) دارید که میدانید یک مقدار صحیح از نوع enum است، میتوانید به سادگی به کامپایلر بگویید که مقدار را به عنوان یک enum در نظر بگیرد.
توجه: این تنها زمانی کار میکند که شما میدانید مقدار شما برای این شمارش معتبر است.
enum Fruit {
Apple = 'apple',
Bananna = 'bananna'
}
const str: string = 'apple';
const fruit: Fruit = str as Fruit;
if (fruit === Fruit.Apple)
console.log("It's an apple");
if (fruit === Fruit.Bananna)
console.log("It's a bananna");
خروجی: این یک سیب است
پاسخ شماره ۳
اکنون می توانید ویژگی ها و کلیدهای آنها را از enums درون انواع در TypeScript دریافت کنید 🎉🎉
enum ApiMessage {
logged_ok = 'Logged OK',
register_ok = 'Register OK'
}
// "logged_ok" | "register_ok"
type ApiMessageTraits = keyof typeof ApiMessage;
// "Logged OK" | "Register OK"
type MessageContents = `${ApiMessage}`;
متأسفانه، این وابسته به تبدیل کلیدهای مجموعههای قابل شمارش به رشته است، بنابراین نمیتوانید هیچ نوع کلیدی جز رشته داشته باشید.
ویرایش: روش دوم را بهطور قابل توجهی با استفاده از template literals در TypeScript >=4.1 سادهسازی کردم.
ویرایش: شما اکنون همچنین میتوانید مقادیر enum با مقادیر عددی را از طریق تجزیه یک رشته عددی در عبارات extends دریافت کنید:
enum ApiMessage {
logged_ok = 1,
register_ok = 2,
}
// "logged_ok" | "register_ok"
type ApiMessageTraits = keyof typeof ApiMessage;
// 1 | 2
type MessageContents = `${ApiMessage}` extends `${infer N extends number}` ? N : never;
پاسخ شماره ۴
شما میتوانید بهراحتی یک نقشه ایجاد کنید که به شما اجازه میدهد کلید را از مقدار بهدست آورید بدون اینکه یک تابع خاص برای آن ایجاد کنید.
export enum ApiMessage {
logged_ok = 'Logged OK',
register_ok = 'Register OK'
}
export type ApiMessageKey = keyof typeof ApiMessage;
export const API_MESSAGE_KEYS = new Map<ApiMessage, ApiMessageKey>(
Object.entries(ApiMessage).map(([key, value]:[ApiMessageKey, ApiMessage]) => [value, key])
)
API_MESSAGE_KEYS.get(ApiMessage.logged_ok); // 'logged_ok'
