skip to Main Content

I have the following types:

type Settings = {
    enableAdvancedFeature?: boolean;
};

type Options = {
    settings?: Settings;
    enforceAdvancedOrder?: boolean;
};

I want to make the enforceAdvancedOrder property a boolean only when settings.enableAdvancedFeature is true.

If settings.enableAdvancedFeature is false or undefined, then enforceAdvancedOrder should be undefined.

Here’s an example of the desired behavior:

const validOption1: Options = {
    settings: { enableAdvancedFeature: true },
    enforceAdvancedOrder: true,
};

const validOption2: Options = {
    settings: { enableAdvancedFeature: false },
    enforceAdvancedOrder: undefined, // enforceAdvancedOrder should be undefined
};

const invalidOption: Options = {
    settings: { enableAdvancedFeature: false },
    enforceAdvancedOrder: true, // This should cause a TypeScript error
};

I tried defining a conditional type for Options but couldn’t figure out how to enforce this dependency between the nested properties.

How can I define the Options type to achieve this behavior while keeping it type-safe? Any suggestions would be greatly appreciated!

2

Answers


  1. Just like that

    type ConditionalOption =
        | { enforceAdvancedOrder?: boolean, settings?: { enableAdvancedFeature?: true } }
        | { enforceAdvancedOrder?: undefined, settings?: { enableAdvancedFeature?: false | undefined } }
    

    examples

    let a: ConditionalOption = {
        settings: {
            enableAdvancedFeature: true
        },
        enforceAdvancedOrder: true
    } // okay
    
    let b: ConditionalOption = {
        settings: {
            enableAdvancedFeature: false
        },
        enforceAdvancedOrder: true
    } // throws error
    
    let c: ConditionalOption = {
        settings: {
            enableAdvancedFeature: false
        },
        enforceAdvancedOrder: undefined
    } // okay
    
    Login or Signup to reply.
  2. You can use a union type that allows enforceAdvancedOrder to be a boolean if enableAdvancedFeatures is set to true and otherwise requires that enforceAdvancedOrder is undefined.

    Here’s how:

    type Options = ({
        settings?: Settings<true>;
        enforceAdvancedOrder?: boolean;
    } | {
        settings?: Settings<false | undefined>;
        enforceAdvancedOrder?: never;
    });
    
    type Settings<B extends boolean | undefined = boolean> = B extends undefined ? {
        enableAdvancedFeature?: B;
    } : {
        enableAdvancedFeature: B;
    };
    

    Here are some samples to verify:

    const validOption1: Options = {
        settings: { enableAdvancedFeature: true },
        enforceAdvancedOrder: true,
    };
    
    const validOption2: Options = {
        settings: { enableAdvancedFeature: false },
        enforceAdvancedOrder: undefined, // enforceAdvancedOrder should be undefined
    };
    
    const validOption3: Options = {
        settings: {},
        enforceAdvancedOrder: undefined, // enforceAdvancedOrder should be undefined
    };
    
    const invalidOption: Options = {
        settings: { enableAdvancedFeature: false },
        enforceAdvancedOrder: true, // This should cause a TypeScript error
    };
    
    const invalidOption2: Options = {
        settings: {},
        enforceAdvancedOrder: true, // This should cause a TypeScript error
    };
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search