Home > database >  Typescript type with multiple conditions
Typescript type with multiple conditions

Time:10-14

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"

CodePudding user response:

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

  • Related