Búsqueda de sitios web

redux - ¿Por qué TypeScript me obliga a pasar explícitamente undefined para un argumento que está tipado como undefined en la firma de mi función?


Soy un desarrollador junior y necesito ayuda. He intentado buscar el problema en Google, pero me cuesta formularlo de la manera correcta. Disculpa de antemano si la pregunta es confusa.

¿Puede alguien ayudarme a entender por qué TypeScript me obliga a pasar undefined como argumento a una función cuando tengo el parámetro tipado como undefined en la firma de la función?

Esto es lo que estoy tratando de hacer. Quiero crear una función que devuelva funciones creadoras de acciones, que luego devuelvan objetos de acción:

interface IAction<T, P> {
    type: T;
    payload?: P;
}

const createActionCreator = <T extends string, P = undefined>(type: T) => (payload: P): IAction<T, P> => ({
    type,
    payload,
});

const THIS_IS_MY_ACTION_TYPE = '@PREFIXED/THIS_IS_MY_ACTION_TYPE';

const createTestAction1 = createActionCreator<typeof THIS_IS_MY_ACTION_TYPE, string>(THIS_IS_MY_ACTION_TYPE);

const createTestAction2 = createActionCreator<typeof THIS_IS_MY_ACTION_TYPE>(THIS_IS_MY_ACTION_TYPE);

const testAction1 = createTestAction1('some string');
// works fine

const testAction2 = createTestAction2();
// TypeScript complains that an argument was expected

Cuando invoco createActionCreator proporcionando tanto los tipos T como P, TypeScript me obliga a invocar createTestAction1 con un argumento que coincida con el tipo P. Esto es lo que espero.

Cuando invoco createActionCreator sin proporcionar el P, o incluso si proporciono el P como undefined explícitamente, TypeScript me obliga a invocar createTestAction2 con undefined como argumento. Esperaría poder invocarlo sin un argumento.

Agradecería si alguien pudiera ayudarme a entender por qué se comporta de esta manera y cómo solucionarlo. Gracias de antemano.

Respuesta #1

La función (payload: P) => {..} tiene un parámetro, por lo que Typescript espera un argumento, incluso si el tipo de parámetro es undefined. Intenta hacer que el parámetro sea opcional (usando ?), y luego elimina el tipo por defecto = undefined de tu parámetro de tipo <P>.

const createActionCreator = <T extends string, P>(type: T) =>
//                                             ^ type `P` is required
    (payload?: P): IAction<T, P> => ({
    //      ^ parameter `payload` is optional
        type,
        payload, // if not provided, will be the value `undefined`
    });

Respuesta #2

Pude lograr lo que quería con la sobrecarga de firmas de funciones y un argumento payloadRequired en la primera invocación, para determinar si devolver la función interna con o sin un parámetro de payload:

export interface IAction<T extends IActionConstant, P = undefined> {
    type: T;
    payload?: P;
}

export function createActionCreator<T extends IActionConstant, P = 'Please provide a type definition for the payload'>(
    type: T,
    config: { withPayload: true }
): (payload: P) => IAction<T, P>;
export function createActionCreator<T extends IActionConstant>(
    type: T,
    config: { withPayload: false }
): () => IAction<T>;
export function createActionCreator<T extends IActionConstant, P>(type: T, config: { withPayload: boolean }) {
    if (config.withPayload) {
        return function createAction(payload: P) {
            return {
                type,
                payload,
            };
        };
    }
    return function createAction() {
        return {
            type,
        };
    };
}