TS динамический интерфейс
Я пишу игру про майнинг, и мне нужно работать с разными типами комплектующих: GPU, platform, RAM, PSU, case. У всех много различных полей. И в БД я решил сделать одну таблицу parts, где перечислены все характеристики всех комплектующих.
И если в БД запись с типом GPU, то заполнены поля ТОЛЬКО для GPU, а остальные NULL.
То есть поля для Part зависят от Part.type. Как описать такое поведение через интерфейс или класс?
Вот мои интерфейсы.
export type PartType = 'GPU' | 'platform' | 'RAM' | 'PSU' | 'case';
export interface IBasePart {
id?: number;
name: string;
image: string;
vendor: string;
slug: string;
type: PartType;
price: number; // In $$$
created_at?: string;
updated_at?: string;
_image?: File | null;
}
export interface IGPU extends IBasePart {
GPU_VRAM_size: number;
GPU_VRAM_frequency: number;
GPU_VRAM_type: number;
GPU_fans_count: number;
GPU_fans_efficiency: number;
}
export interface IPlatform extends IBasePart {
platform_cors_count: number;
platform_threads_count: number;
platform_frequency: number;
platform_RAM_slots: number;
}
export interface IRAM extends IBasePart {
RAM_frequency: number;
RAM_size: number;
RAM_channels: number;
}
export type PSU_EfficiencyType = 'none' | 'bronze' | 'silver' | 'gold' | 'platinum' | 'titanium';
export interface IPSU extends IBasePart {
PSU_power_supply: number; // WATT
PSU_efficiency: PSU_EfficiencyType;
}
export type CaseMaterialType = 'wood' | 'iron' | 'aluminium';
export interface ICase extends IBasePart {
case_material: CaseMaterialType;
case_material_rus: string;
case_GPUs_slots: number;
case_critical_temp: number;
}
// -----------------------
export interface IPart extends IBasePart, IGPU, IPlatform, IRAM, IPSU, ICase {
}
// Или, может, так
export type PartType<T> = T & IBasePart;
Ответы (2 шт):
Вам скорее всего нужно прочитать эту часть документации. Там очень подробно расписано как наследовать нужный интерфейс в зависимости от условий
Как один из вариантов, можете попробовать сделать так:
interface IGPU {
one: number
}
interface IPlatform {
two: number
}
interface IRAM {
three: number
}
type Part<T extends 'IGPU' | 'IPlatform' | 'IRAM'> =
T extends 'IGPU' ? IGPU :
T extends 'IPlatform' ? IPlatform : IRAM;
const part1: Part<'IGPU'> = {one: 1};
const part2: Part<'IPlatform'> = {two: 2};
const part3: Part<'IRAM'> = {three: 3};
В данном случае стоит воспользоваться discriminated unions
для этого нужно убрать из общей части поле type, по которой можно определить конкретный тип, и добавить его с нужным значением в конкретные типы
пример:
export interface IBasePart {
id?: number;
name: string;
image: string;
vendor: string;
slug: string;
price: number; // In $$$
created_at?: string;
updated_at?: string;
_image?: File | null;
}
export interface IGPU {
type: 'GPU';
GPU_VRAM_size: number;
GPU_VRAM_frequency: number;
GPU_VRAM_type: number;
GPU_fans_count: number;
GPU_fans_efficiency: number;
}
export interface IPlatform {
type: 'platform';
platform_cors_count: number;
platform_threads_count: number;
platform_frequency: number;
platform_RAM_slots: number;
}
export type Part = IBasePart & (IGPU | IPlatform) // Конечный тип
function fun(p : Part){
console.log(p.id); // base fields
if (p.type === 'platform') {
console.log(p.platform_cors_count) // fields from IPlatform
}else {
console.log(p.GPU_fans_count) // fields from IGPU
}
}