Home > Software design >  Type with at least one of 2 parameters
Type with at least one of 2 parameters

Time:10-06

I have a type with an id field (string) and an oldId field (number). But I don't want to have it in the same time

ex.

{ id: "1234", name: "foo" }

{ oldId: 1234, name: "bar" }

I tried this :

export type WithId<T> = T & ({ id: string } | { oldId: number })
export type Product = WithId<{
  name: string
}>

const product1: Product = {
  oldId: 12345,
  name: "foo"
}

const product2: Product = {
  id: "12345",
  name: "bar"
}

This is working. But when it's a parameter, it's not working :

import { Product } from 'product'

export const someTest = (product: Product) => {
  return product.id
}

I'm getting :

Property 'id' does not exist on type 'Product'.

CodePudding user response:

You will need to combine a never with a union similar to the example below.

The first part of the type { name: string } is the "common" definition. This could be one of more properties.

Then this is followed by a union of "an id of type string but never an oldId" OR "an oldId of type number, but never an id". Note that the never's need a ? in this case

It's a very useful little pattern.

const product1 = { id: '1234', name: 'foo' };
const product2 = { oldId: 1234, name: 'bar' };
const product9 = { id: '1234', oldId: 1234, name: 'bar' };

type Product = { name: string } & (
  | {
      id: string;
      oldId?: never;
    }
  | {
      id?: never;
      oldId: number;
    }
);

const printProduct = (product: Product) => {
    console.log(product.name);
}

printProduct(product1);
printProduct(product2);
printProduct(product9); // <-- won't be accepted, because product9 has both an id and an oldId

Playground here

  • Related