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

javascript - การโคลนชนิดข้อมูลและการใช้คีย์ของมัน


ฉันรู้ว่าฟังดูเหมือนเป็นไอเดียของมอนสเตอร์แฟรงเกนสไตน์ แต่ว่าโปรดฟังฉันก่อน ฉันมีสเปกนะ:

export enum detailsDataTypes {
  MACHINE = 'MACHINE',
  USER = 'USER',
  ABSTRACT = 'ABSTRACT',
}

export type sharedTypes = {
  name?: string;
  OEECategory?: string;
  OAECategory?: string;
  color?: string;
};

export type AbstractData = sharedTypes & {
  layer: string;
  type: detailsDataTypes.ABSTRACT;
};

export type UserData = sharedTypes & {
  defaultTimeout: number;
  stateAfterTimeout: string;
  accessCanChoose: string;
  accessCanOverwrite: string;
  type: detailsDataTypes.USER;
};

export type MachineData = Omit<UserData, 'type'> & {
  stateCategory: string;
  canOverwriteOnlyByStates: boolean;
  machineType: string;
  type: detailsDataTypes.MACHINE;
};

และประเภทนี้เหมาะสำหรับเก็บข้อมูล แต่ฉันต้องการอีกประเภทหนึ่งสำหรับเก็บหัวเรื่อง (ที่มนุษย์อ่านได้) นี่คือไอเดียของฉัน:

const sharedHeaders: sharedTypes = {
  name: 'Name',
  OEECategory: 'OEE category',
  OAECategory: 'OAE category',
  color: 'Color',
};

export const AbstractHeaders: Omit<AbstractData, 'type'> = { ...sharedHeaders, layer: 'Layer' };

export const UserHeaders: Omit<UserData, 'type'> | { defaultTimeout: string } = {
  ...sharedHeaders,
  defaultTimeout: 'Default timeout',
  stateAfterTimeout: 'State after timeout',
  accessCanChoose: 'Access - can choose',
  accessCanOverwrite: 'Access - can overwrite',
};

export const MachineHeaders: Omit<MachineData, 'type'> = {
  ...UserHeaders,
  stateCategory: 'State category',
  machineType: 'Machine type',
  canOverwriteOnlyByStates: 'Can overwrite only by states',
};

แต่ที่นี่เรามีความขัดแย้งของประเภทหลายอย่าง และฉันเลี่ยงไม่ได้ที่จะคิดว่าต้องมีวิธีที่ดีกว่านี้ในการทำสิ่งนี้

ตัวอย่างเช่นที่นี่ ฉันเข้าใจว่าทุกอย่างหลังจากการทำลายโครงสร้างเป็นสตริง ซึ่งไม่สามารถกำหนดค่าให้กับประเภท never ได้

export const MachineHeaders: Omit<MachineData, 'type'> = {
  ...UserHeaders,
  stateCategory: 'State category', //here
  machineType: 'Machine type', //here
  canOverwriteOnlyByStates: 'Can overwrite only by states', //here
};

มีวิธีที่ดีกว่าในการบรรลุเป้าหมายของฉันหรือไม่ หรือฉันกำลังคิดเกี่ยวกับมันในทางที่ผิด?

คำตอบ #1

เมื่อคุณมีประเภทวัตถุและต้องการสร้างประเภทวัตถุใหม่จากมันโดยใช้โปรแกรม คุณมักจะสามารถใช้ประเภทที่แมปแล้วในการทำเช่นนั้นได้


ในกรณีของคุณ คุณมีชุดประเภทอ็อบเจ็กต์ที่ค่าของคุณสมบัติเป็นแบบใดก็ได้ และคุณต้องการสร้างเวอร์ชันใหม่ที่มีคีย์เดิมแต่คุณสมบัติทั้งหมดเป็น string เพื่อใช้เป็นหัวตารางของค่าหรือบางอย่าง

เราสามารถทำแบบนี้ได้:

type HeadersFor<T> = { [K in keyof T]: string };

ลองทดสอบมันดู:

type X = HeadersFor<{ a: number, b: boolean, c?: Date, type: string }>;
/* type X = {
  a: string;
  b: string;
  c?: string | undefined;
  type: string;
} */

ตามที่คุณเห็น คุณสมบัติแต่ละอย่างถูกแปลงเป็น string แล้ว ดีแล้ว, ฟิลด์ c เป็นฟิลด์ที่สามารถเลือกได้ในชนิดข้อมูลอินพุต และมันยังคงเป็นเช่นนั้นในชนิดข้อมูลเอาต์พุต (ดังนั้นจึงรวมถึง undefined เป็นความเป็นไปได้ด้วย) ถ้าคุณต้องการให้คุณสมบัติในเอาต์พุตเป็นแบบบังคับและไม่สามารถเลือกได้ คุณสามารถใช้ตัวแก้ไขแผนที่เพื่อให้ได้ผลลัพธ์นั้น:

type HeadersFor<T> = { [K in keyof T]-?: string };

type X = HeadersFor<{ a: number, b: boolean, c?: Date, type: string }>;
/* type X = {
  a: string;
  b: string;
  c: string
  type: string;
} */

เยี่ยม! แต่เมื่อดูจากต้นฉบับของคุณ ดูเหมือนว่าคุณต้องการละเว้นคุณสมบัติ type ทั้งหมดด้วย คุณสามารถรวม Omit กับ HeadersFor ได้ถ้าต้องการ หรือคุณอาจนิยาม HeadersFor ใหม่เพื่อให้อัตโนมัติไม่รวมคีย์ type จากชนิดที่แมป มีหลายวิธีในการทำเช่นนั้น แต่ตั้งแต่ TS4.1 ผมคงจะแนะนำให้ใช้การแมปคีย์ใหม่ผ่าน as แบบนี้:

  type HeadersFor<T> = { [K in keyof T as Exclude<K, "type">]-?: string };

  type X = HeadersFor<{ a: number, b: boolean, c?: Date, type: string }>;
  /* type X = {
    a: string;
    b: string;
    c: string
  } */

นั่นดูเหมือนทุกสิ่งที่คุณต้องการ ดังนั้นเราจะเก็บคำนิยามของ HeadersFor ไว้


ตอนนี้คุณสามารถทำสิ่งนี้ได้โดยไม่มีข้อผิดพลาด:

const sharedHeaders: HeadersFor<SharedTypes> = {
  name: 'Name',
  OEECategory: 'OEE category',
  OAECategory: 'OAE category',
  color: 'Color',
};

export const AbstractHeaders: HeadersFor<AbstractData> = { ...sharedHeaders, layer: 'Layer' };

export const UserHeaders: HeadersFor<UserData> = {
  ...sharedHeaders,
  defaultTimeout: 'Default timeout',
  stateAfterTimeout: 'State after timeout',
  accessCanChoose: 'Access - can choose',
  accessCanOverwrite: 'Access - can overwrite',
};

export const MachineHeaders: HeadersFor<MachineData> = {
  ...UserHeaders,
  stateCategory: 'State category',
  machineType: 'Machine type',
  canOverwriteOnlyByStates: 'Can overwrite only by states',
};

ลิงก์ Playground ไปยังโค้ด