Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Typescript generic destructor

What I have

interface Tenant {
  id: number;
  name: string;
}

interface Server {
  id: number;
  location: string;
  ip: string;
}

interface User {
  id: number;
  name: string;
  tenant: number | Tenant;
  server: number | Server;
}

What I want

Properties tenant and server could be "populated" but by default, they are not. I want to be able to define the type and specify which properties are populated, like this:

const user: User; // both tenant and server are number
const user2: User<{ tenant: Tenant }>; // tenant is populated, server - not
const user3: User<{ tenant: Tenant; server: Server }>; // both props are populated

What did I try


interface UserPopulate {
  tenant: Tenant | number;
  server: Server | number;
}

interface DefaultUserPopulate {
  tenant: number;
  server: number;
}

export interface User2<Populate extends UserPopulate = DefaultUserPopulate>{
  id: number;
  name: string;
  tenant: Populate['tenant'];
  server: Populate['server'];
}

const user: User<{ tenant: number }>;

But Typescript requires me to define both tenant and server (User<{ tenant: number; server: number }> or nothing (User), but I want to be able to override only one "populate"

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

>Solution :

The answer is conditional types:

interface User<T = {}> {
  id: number;
  name: string;
  tenant: T extends {tenant: Tenant} ? Tenant : number;
  server: T extends {server: Server} ? Server : number;
}

TS playground

But a better approach would be to simplify T as the object type is not carrying any useful information, so a simple string union would do.

TS playground

interface User<T extends "tenant" | "server" | "default" = "default"> {
  id: number;
  name: string;
  tenant: "tenant" extends T ? Tenant : number;
  server: "server" extends T ? Server : number;
}

let user: User; // both tenant and server are number
let user2: User<"tenant">; // tenant is populated, server - not
let user3: User<"tenant" | "server">; // both props are populated

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading