简体   繁体   中英

Return object of functions with argument types of incoming function argument values

I'd like to have function, let's call it DummyService that would accept some definition object as argument and return bunch of functions in a way that arguments of those returned functions can only accept argument values that has been passed to DummyService as an argument. See the code example bellow. Any idea how to achieve this? Thanks a lot in advance.

type Definition = {
    id: string;
    steps: Array<{key: string, callback?: () => void}>
};

const DummyService = (definition: Definition) => {
    return { 
        dummyFn: (step: {key: any}) => { // I need to figure out the type that would allow to pass only certain values that has been passed in `steps` array of `definition`
            console.log(step);
        }
    };
};

const DummyComponent1 = () => {
    const definition = {
        id: "testId1",
        steps: [
            {  key: "key1"},
            {  key: "key2"},
        ],
    };

    const { dummyFn } = DummyService(definition);
    const handler = () => dummyFn({ key: "key1dsfasd" }); // I want this to be invalid - valid key values should be only "key1" or "key2"

    return <div onClick={handler}>test</div>;
}

const DummyComponent2 = () => {
    const definition = {
        id: "testId2",
        steps: [
            {  key: "key3"},
            {  key: "key4"},
        ],
    };

    const { dummyFn } = DummyService(definition);
    const handler = () => dummyFn({ key: "key3" }); // This should be valid - valid key values should be only "key2" or "key4"

    return <div onClick={handler}>test</div>;
}```

We need to make Definition an generic that depends on the array of steps.

The tricky, messy part here is dealing with strings like "key1" and "key2" as their literal values instead of just type string . In order to do that, we must add as const after the definition

const definition = {/*...*/} as const;

This interprets the values as literal strings, but it also makes everything in the definition readonly . So we need to add a bunch of readonly when defining the types for Definition and DummyService or else we will get errors about assigning readonly to a mutable type.

A definition is defined like this:

type Definition<Steps extends readonly {key: string, callback?: () => void}[]> = {
    readonly id: string;
    readonly steps: Steps;
};

We say that our Steps extends an array, and we want it to be a specific tuple.

Our DummyService looks like this:

const DummyService = <Steps extends readonly {key: string, callback?: () => void}[]>(definition: Definition<Steps>) => {
    return { 
        dummyFn: (step: { key: Steps[number]['key'] }) => {
            console.log(step);
        }
    };
};

That type Steps[number]['key'] gives us all valid step keys. As a sidenote, I'm not sure why you want to pass your args like ({key: "key2"}) instead of just ("key2") .

You now get all of the desired errors, and no errors when calling correctly.

Typescript Playground Link

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM