ค้นหาเว็บไซต์

angular - ใช้วัตถุแบบ literal เป็นค่าของ enum ใน TypeScript


ฉันมีตัวแปรประเภท enum:

export enum PizzaSize {
  SMALL =  0,
  MEDIUM = 1,
  LARGE = 2
}

แต่ตรงนี้ฉันอยากจะใช้ค่าคู่บางคู่: เช่น SMALL ฉันอยากจะบอกว่ามันมี key เป็น 0 และ value เป็น 100 ฉันตั้งใจที่จะใช้:

export enum PizzaSize {
  SMALL =  { key: 0, value: 100 },
  // ...
}

แต่ TypeScript ไม่ยอมรับอันนี้ ฉันจะทำแบบนี้ได้อย่างไร?

คำตอบ #1

TypeScript รองรับเฉพาะ enum ที่เป็นตัวเลขหรือตัวอักษรเท่านั้น ดังนั้นคุณต้องเลียนแบบ enum ของวัตถุด้วยคลาส (ซึ่งจะช่วยให้คุณสามารถใช้มันเป็นประเภทในประกาศฟังก์ชันได้):

export class PizzaSize {
  static readonly SMALL  = new PizzaSize('SMALL', 'A small pizza');
  static readonly MEDIUM = new PizzaSize('MEDIUM', 'A medium pizza');
  static readonly LARGE  = new PizzaSize('LARGE', 'A large pizza');

  // private to disallow creating other instances of this type
  private constructor(private readonly key: string, public readonly value: any) {
  }

  toString() {
    return this.key;
  }
}

จากนั้นคุณสามารถใช้อินสแตนซ์ที่กำหนดไว้ล่วงหน้าเพื่อเข้าถึง value ของพวกมัน:

const mediumVal = PizzaSize.MEDIUM.value;

หรือคุณสมบัติ/ประเภทของคุณสมบัติอื่น ๆ ที่คุณอาจต้องการกำหนดใน PizzaSize

และขอบคุณการแทนที่ toString() คุณจะสามารถพิมพ์ชื่อ/คีย์ของ enum ได้โดยอัตโนมัติจากวัตถุ:

console.log(PizzaSize.MEDIUM);  // prints 'MEDIUM'

คำตอบ #2

อัปเดต: ดูคำตอบของ @Javarome ด้านล่าง ซึ่งสง่างามกว่าครับ ผมแนะนำให้ใช้วิธีของเขาครับ

ถ้าคุณต้องการใช้ Type ลองเพิ่มโค้ดบางส่วน การใช้งาน: getPizzSizeSpec(PizzaSize.small).value

enum PizzaSize {
    small,
    medium,
    large
}
interface PizzaSizeSpec {
    key: number,
    value: number
}
function getPizzaSizeSpec(pizzaSize: PizzaSize): PizzaSizeSpec {
    switch (pizzaSize) {
        case PizzaSize.small:
            return {key:0, value: 25};
        case PizzaSize.medium:
            return {key:0, value: 35};
        case PizzaSize.large:
            return {key:0, value: 50};
    }
}

คำตอบ #3

เริ่มตั้งแต่ Typescript 3.4 เป็นต้นไป คุณสามารถใช้การผสมผสานระหว่าง keyof typeof และ const assertions เพื่อสร้างอ็อบเจ็กต์ที่สามารถมีความปลอดภัยในเรื่องประเภทเหมือนกับ enums และยังคงเก็บค่าที่ซับซ้อนได้

โดยการสร้าง type ที่มีชื่อเดียวกับ const คุณสามารถมีการตรวจสอบความครบถ้วนเหมือนกับที่ enums ปกติมีได้

ปัญหาเดียวคือคุณต้องมีคีย์บางอย่างในอ็อบเจ็กต์ซับซ้อน (ฉันใช้ value ที่นี่) เพื่อเก็บชื่อของสมาชิก enum (ถ้าใครสามารถหาฟังก์ชันช่วยที่สามารถสร้างอ็อบเจ็กต์เหล่านี้ในแบบ typesafe ได้ ฉันอยากเห็นมันมาก! ฉันไม่สามารถทำให้มันทำงานได้)

export const PizzaSize = {
    small: { value: 'small', key: 0, size: 25 },
    medium: { value: 'medium', key: 1, size: 35 },
    large: { value: 'large', key: 2, size: 50 },
} as const

export type PizzaSize = keyof typeof PizzaSize

// if you remove any of these cases, the function won't compile
// because it can't guarantee that you've returned a string
export function order(p: PizzaSize): string {
    switch (p) {
        case PizzaSize.small.value: return 'just for show'
        case PizzaSize.medium.value: return 'just for show'
        case PizzaSize.large.value: return 'just for show'
    }
}

// you can also just hardcode the strings,
// they'll be type checked
export function order(p: PizzaSize): string {
    switch (p) {
        case 'small': return 'just for show'
        case 'medium': return 'just for show'
        case 'large': return 'just for show'
    }
}

ในไฟล์อื่น ๆ สามารถใช้สิ่งนี้ได้อย่างง่ายดาย เพียงแค่ import PizzaSize

import { PizzaSize } from './pizza'

console.log(PizzaSize.small.key)

type Order = { size: PizzaSize, person: string }

สังเกตด้วยว่าแม้แต่วัตถุที่มักจะสามารถเปลี่ยนแปลงค่าได้ ก็ไม่สามารถถูกเปลี่ยนแปลงได้ด้วยไวยากรณ์ as const

const Thing = {
    ONE: { one: [1, 2, 3] }
} as const

// this won't compile!! Yay!!
Thing.ONE.one.splice(1, 0, 0)

คำตอบ #4

ฉันคิดว่าในการที่จะได้สิ่งที่คุณต้องการ แบบนี้น่าจะใช้ได้

interface PizzaInfo {
  name: string;
  cost_multiplier: number;
}

enum PizzaSize {
  SMALL,
  MEDIUM,
  LARGE,
}

const pizzas: Record<PizzaSize, PizzaInfo> = {
  [PizzaSize.SMALL]: { name: "Small", cost_multiplier: 0.7 },
  [PizzaSize.MEDIUM]: { name: "Medium", cost_multiplier: 1.0 },
  [PizzaSize.LARGE]: { name: "Large", cost_multiplier: 1.5 },
};

const order = PizzaSize.SMALL;
console.log(pizzas[order].name);  // "Small"

บทความที่เกี่ยวข้อง