1

I have only been using TypeScript a couple months, and I just noticed that the compiler is not enforcing the shape of data a function accepts if that function is accessed through React.useContext().

This setup here is not exactly what I have going on, but it more or less shows the problem I am trying to figure out.

import * as React from 'react';

//==>> Reducer State Interface
interface InitialStateInterface {
    handleSettingFooBar: Function;
    foo: string | null;
    bar: boolean;
}

//==>> Function Interface
interface PayloadInterface {
    foo?: string | null;
    bar?: boolean;
}

//==> Reducer
interface ReducerInterface {
    type: string;
    payload: PayloadInterface;
}

enum ActionTypes {
    SET_FOO = 'SET_FOO',
    SET_BAR = 'SET_BAR',
}

const initialState: InitialStateInterface = {
    handleSettingFooBar: () => null,
    foo: null,
    bar: false,
};

const SomeContext = React.createContext<InitialStateInterface>(initialState);

const ContextReducer = (state: any, { type, payload }: ReducerInterface) => {
    switch (type) {
        case ActionTypes.SET_FOO:
            return { ...state, foo: payload.foo };
        case ActionTypes.SET_BAR:
            return { ...state, bar: payload.bar };
        default:
            return state;
    }
};

const SomeProvider = () => {
    const [state, dispatch] = React.useReducer(ContextReducer, initialState);

    function handleSettingFooBar(data: PayloadInterface) {
        let { foo, bar } = data;
        if (foo) dispatch({ type: ActionTypes.SET_FOO, payload: { foo } });
        if (bar) dispatch({ type: ActionTypes.SET_BAR, payload: { bar } });
    }

        /** Okay, of course, no error */
    handleSettingFooBar({ foo: 'test' }); 

        /** Error as expected, type does not match */
    handleSettingFooBar({ foo: false }); 

        /** Error as expected, key does not exist */
    handleSettingFooBar({ randomKey: 'cant do this' }); 

    return <SomeContext.Provider value={{ ...state, handleSettingFooBar }} />;
};


/* ===>  But when writing a component that uses that context <=== */

export const SomeComponent = () => {
    const { handleSettingFooBar } = React.useContext(SomeContext);

        /** Why is the compiler not yelling at me here??? */
    handleSettingFooBar({ randomKey: 'hahaha' }); 
};

export { SomeProvider, SomeContext };

I have tried putting the interface in when calling the context, like this:

const { handleSettingFooBar } = React.useContext<InitialStateInterface>(SomeContext);

But that made no difference.

I am expecting that if somebody is authoring a component that uses this context and its provided functions, that it will regulate the data (at compile time, of course) they try to pass in so a generic setter may not add a value that does not belong in the context reducer state.

Please help, thanks!

1 Answer 1

1

The SomeContext has the InitialStateInterface type which defines handleSettingFooBar as handleSettingFooBar: Function, and it does not know how you actually implemented it.

You can change that to handleSettingFooBar: (data:PayloadInterface) => void and then the typescript would know what kind of input should be allowed for it.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.