DEV Community

Chris Cook
Chris Cook

Posted on • Edited on • Originally published at zirkelc.dev

5 2 2 2 2

Optional vs. Undefined: How To Check for Optional Properties

In TypeScript, a property is considered optional if it can be omitted from an object, meaning it can be either undefined or not provided at all. Optional properties are denoted using the ? suffix on the property key. Determining whether a property is optional or explicitly defined with undefined as its type can be quite tricky.

Let's consider the following example with five possible combinations:

type Example = {
    required: number;
    optional?: number;
    requiredAsUndefined: undefined;
    requiredWithUndefined: number | undefined;
    optionalWithUndefined?: number | undefined;
}
Enter fullscreen mode Exit fullscreen mode

The last four properties are allowed to be undefined, but only the second and fifth are actually optional. Interestingly, the properties optional, requiredWithUndefined, and optionalWithUndefined all resolve to the same union type number | undefined.

So, what we want is a type that returns true for optional and optionalWithUndefined, and false for the rest. Here's how such a utility type can look:

type IsOptional<T, K extends keyof T> = undefined extends T[K]
    ? ({} extends Pick<T, K> ? true : false)
    : false;

type Required = IsOptional<Example, 'required'>;  // false
type Optional = IsOptional<Example, 'optional'>;  // true
type RequiredAsUndefined = IsOptional<Example, 'requiredAsUndefined'>;  // false
type RequiredWithUndefined = IsOptional<Example, 'requiredWithUndefined'>;  // false
type OptionalWithUndefined = IsOptional<Example, 'optionalWithUndefined'>;  // true
Enter fullscreen mode Exit fullscreen mode

There are two constraints in this utility type. The first constraint, undefined extends T[K], checks if undefined can be a part of the type accessed by T[K]. It essentially asks whether the type T[K] can include undefined. The second constraint, {} extends Pick<T, K> ? true : false, ensures that the type {} (an empty object) is assignable to a type where the property K is picked, implying the property is optional.

From this utility type, we can build a new mapped type which only picks optional properties. The non-optional properties will be set to never:

type OptionalProperties<T> = {
    [K in keyof T]: IsOptional<T, K> extends true 
        ? T[K] 
        : never
}

type OnlyOptionals = OptionalProperties<Example>;
// type OnlyOptionals = {
//     required: never;
//     optional?: number;
//     requiredAsUndefined: never;
//     requiredWithUndefined: never;
//     optionalWithUndefined?: number | undefined;
// }
Enter fullscreen mode Exit fullscreen mode

Having the properties with type never is usually enough for type safety, but in case we truly want to omit these properties for stylistic purposes, we can move the IsOptional<T, K> extends true constraint into the square brackets. This is a nice little trick because it will set the key of non-optional properties to never, which will then get omitted by TypeScript automatically.

type OnlyOptionals<T> = {
    [K in keyof T as IsOptional<T, K> extends true ? K : never]: T[K]
} 
type OnlyOptionals = OnlyOptionals<Example>;
// type OnlyOptionals = {
//    optional?: number;
//    optionalWithUndefined?: number | undefined;
// }
Enter fullscreen mode Exit fullscreen mode

Here's the Playground to try it out directly in the browser: TypeScript Playground

SurveyJS custom survey software

Simplify data collection in your JS app with a fully integrated form management platform. Includes support for custom question types, skip logic, integrated CCS editor, PDF export, real-time analytics & more. Integrates with any backend system, giving you full control over your data and no user limits.

Learn more

Top comments (0)

A Workflow Copilot. Tailored to You.

Pieces.app image

Our desktop app, with its intelligent copilot, streamlines coding by generating snippets, extracting code from screenshots, and accelerating problem-solving.

Read the docs

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay