javascript - Klona en typ och använda dess nycklar
Jag vet att det här låter som en Frankenstein-idé, men hör på mig. Jag har en typ:
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;
};
Och denna typ är bra för att innehålla data. Men jag vill ha en annan typ som håller rubriker (människoläsbara). Detta är min idé:
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',
};
Men här har vi en massa typkonflikter, och jag kan inte låta bli att tänka att det måste finnas ett bättre sätt att göra detta.
Till exempel här förstår jag att allt efter destruktureringen är sträng, vilket inte kan tilldelas typen never.
export const MachineHeaders: Omit<MachineData, 'type'> = {
...UserHeaders,
stateCategory: 'State category', //here
machineType: 'Machine type', //here
canOverwriteOnlyByStates: 'Can overwrite only by states', //here
};
Finns det ett bättre sätt att uppnå mitt mål? Eller tänker jag på det på fel sätt?
Svar #1
När du har en objekttyp och vill programmässigt skapa en ny objekttyp från den, kan du ofta använda en mappad typ för att göra det.
I ditt fall har du en mängd objekttyper vars egenskapsvärdestyper är godtyckliga, och du skulle vilja ta fram nya versioner med samma nycklar men vars egenskaper alla är string, för att använda som rubriker för en tabell med värden eller något liknande.
Vi kan göra det på det här sättet:
type HeadersFor<T> = { [K in keyof T]: string };
Låt oss prova det:
type X = HeadersFor<{ a: number, b: boolean, c?: Date, type: string }>;
/* type X = {
a: string;
b: string;
c?: string | undefined;
type: string;
} */
Som du ser har varje egenskap omvandlats till string. Tja, c-fältet var valfritt i ingångstypen och det förblir det i utgångstypen (och inkluderar därför undefined som en möjlighet). Om du vill att utgångsegenskaperna ska vara obligatoriska och inte valfria, kan du använda en kartläggningsmodifierare för att uppnå det:
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;
} */
Bra! Men om man tittar på din ursprungliga version verkar det som att du också vill utelämna type-egenskapen helt. Du kan kombinera Omit och HeadersFor om du vill, eller så kan du omdefiniera HeadersFor för att automatiskt utesluta en type-nyckel från den mappade typen. Det finns flera sätt att göra det på, men sedan TS4.1 skulle jag antagligen rekommendera att använda nyckelomdefiniering via as på detta sätt:
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
} */
Det ser ut som allt du behöver, så vi kommer att behålla den definitionen av HeadersFor.
Nu kan du göra detta utan fel:
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-länk till kod
