There are a couple of approaches that come to mind.
One option is to define TransferType
based on an object type containing the values like Record<string, TypeFoo>
, as shown here:
interface TransferRecords {
INTERNAL: Record<string, TypeFoo>;
WITHDRAWAL: Record<string, TypeBar>;
DEPOSIT: Record<string, TypeBar>;
}
type TransferType = keyof TransferRecords;
export interface EventsTooltip extends TransferRecords {
some: string;
extra: number;
keys: boolean;
};
Playground Link
This way, both EventsTooltip
and CardType
stay in sync since they reference the same source information (TransferRecords
).
If this approach doesn't fit your needs, another method entails creating a structure where the usage could fail rather than defining the type itself to fail, like so:
type TransferType = "INTERNAL" | "WITHDRAWAL" | "DEPOSIT";
type BaseEventsTooltip = {
some: string;
extra: number;
keys: boolean;
};
export type EventsTooltip = BaseEventsTooltip & {
[key in TransferType]: key extends "INTERNAL"
? Record<string, TypeFoo>
: key extends "WITHDRAWAL"
? Record<string, TypeBar>
: key extends "DEPOSIT"
? Record<string, TypeBar>
: never;
};
Even though this solution may seem cumbersome, it ensures that if you expand TransferType
, any attempt to use tooltip
would result in an error indication necessitating an update to the EventsTooltip
type.
Playground with tooltip
functioning
Playground demonstrating how an additional element in TransferType
can cause tooltip
to fail
Although not the most elegant solution, it effectively serves its purpose. :-)