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 ไปยังโค้ด
