Pierre Ducher

Pierre Ducher

Building the missing utilities in TypeScript

TypeScript gives you Required or NonNullable, but what if you want to apply those to some properties only, or to the properties of some children?


Existing Utilities#

In TypeScript, you have some built-in utilities, for instance Required transforms all the optional properties of a type or interface to non-optional properties. Here is an example from the doc:

interface Props {
  a?: number;
  b?: string;
}

const obj: Props = { a: 5 };

const obj2: Required<Props> = { a: 5 };
Property 'b' is missing in type '{ a: number; }' but required in type 'Required<Props>'.

but what if you want to only make property b required?

The one shot way#

What you want is to pick the property that you have to make non-optionnal, make it required then combine the result with the original interface or type and override the property that is now required. Here is the code:

interface Props {
  a?: number;
  b?: string;
}
interface PropsWithB = Props & Required<Pick<Props, 'b'>>

The advantage of Pick is that it can take multiple property keys, so we can make multiple properties required at once like this:

interface Props {
  a?: number;
  b?: string;
  c?: string;
}
interface PropsWithBandC = Props & Required<Pick<Props, 'b' | 'c'>>

here the interface PropsWithBandC is equivalent to this one

  interface Props {
  a?: number;
  b: string;
  c: string;
}

Building an utility#

Let's make this generic so that we can use it with any type and properties!

/**
 * Make fields required
 * Field names followed by a ? are required
 * (but can take any type/value declared originaly, including undefined or null)
 */
export type RequiredFields<T, K extends keyof T> = T & Required<Pick<T, K>>;

The utility is pretty straight forward if you know the syntax, you can pass an union for K so that you have multiple field required. Here is a usage example

interface Props {
  a?: number;
  b?: string;
  c?: string;
}
interface PropsWithBandC = RequiredFields<Props, 'b' | 'c'>

The resulting interface is equivalent to the one we've done in the one shot solution. You now have a simple utility with the ability to chose which properties you want to make required!