dracodormiens
dracodormiens

Reputation: 520

How to make field available only when optional param was passed?

In the following example, is it possible to make parse() available only if key was passed using TypeScript?

// SOURCE CODE:
interface Input {
    key?: string;
    data: Record<string, string>;
}

interface Options {
    parse: (x: string) => void;
}

type HelperFunc = (options: Options) => void;

const myFunc = (input: Input, helper: HelperFunc): void => {

    // How to implement this such that if key is passed, only then make `parse()` available?
    // CONSTRAINT: I don't want to make `parse()` an optional field because user will call
    // it multiple times and I don't want them to do `parse?.()` all the time.

    if (input.key) {
        helper({ parse: (x) => `${x} parsed using ${input.key}` })
    } else {
        helper({});  // currently throws valid TypeScript error
    }

    // do something with input.data
};

// EXPECTED USAGE:
myFunc({
    key: "my-key",
    data: {
       "someData": "someValue"
    }
}, ({ parse }) => {
    // No TypeScript errors
    parse("parse this");
    parse("parse that");
    parse("parse this and that");
});

// mentioning parse should throw TypeScript error because key was not passed
myFunc({
    data: {
       "someData": "someValue"
    }
}, ({ parse }) => {
    /* either throw error here when calling or while de-structuring above */ 
    parse("parse this");
});

I'm just wondering if this is possible using TypeScript.

Upvotes: 0

Views: 35

Answers (1)

Ovidijus Parsiunas
Ovidijus Parsiunas

Reputation: 2742

You can achieve this by overloading the myFunc function.

First declare two types of interfaces that will be used for each of your parameters. First - ones that have no key:

interface InputData {
    data: Record<string, string>;
}

type EmptyHelperFunc = (options: {}) => void;

And ones where all data is present:

type Input = {
    key?: string;
} & InputData;

interface Options {
    parse: (x: string) => void;
}

type HelperFunc = (options: Options) => void;

The overload type can then be declared and used as follows:

type MyFuncOverload = {
    (input: InputData, helper: EmptyHelperFunc): void
    (input: Required<Input>, helper: HelperFunc): void
}

const myFunc: MyFuncOverload = (input: Input, helper: HelperFunc): void => {

Playground link.

Upvotes: 1

Related Questions