Skip to content

Typescript validation issues when passing function to combiner #445

@sbenescurse

Description

@sbenescurse

So This one was really weird for us to figure out and I think is best described in examples. So below I made a bunch of test cases that show wrong params in all the ways we want to protect our selectors.

import {createSelector} from 'reselect';

interface TestState {
    someNumber: number | null,
    someString: string | null,
}

interface Object1 {
    str: string,
}
interface Object2 {
    num: number,
}

const getNumber = (state: TestState) => state.someNumber;
const getString = (state: TestState) => state.someString;

function generateObject1(str: string): Object1 {
    return {
        str,
    }
}
function generateObject2(num: number): Object2 {
    return {
        num,
    }
}
function generateComplexObject(num: number, subObject: Object1, subObject2: Object2): boolean {
    return true;
}


// ################ Tests ################

// Compact selector examples

// Doesn't error, but should because generateObject1 can't take null
const getObject1 = createSelector(
    [getString],
    generateObject1,
);

// Doesn't error, but should because generateObject2 can't take null
const getObject2 = createSelector(
    [getNumber],
    generateObject2
);

// Doesn't error, but should because mismatch of params
const getComplexObjectTest1 = createSelector(
    [getObject1],
    generateComplexObject
);

// Does error, but error is really weird and talks about "Object1 is not assignable to type number"
const getComplexObjectTest2 = createSelector(
    [
        getNumber,
        getObject1,
    ],
    generateComplexObject
);

// Doesn't error, but should because number can't be null
const getComplexObjectTest3 = createSelector(
    [
        getNumber,
        getObject1,
        getObject2,
    ],
    generateComplexObject
);

// Does error, but error is really weird and talks about "Object1 is not assignable to type number"
const getComplexObjectTest4 = createSelector(
    [
        getObject1,
        getNumber,
        getObject2,
    ],
    generateComplexObject
);



// Verbose selector examples

// Errors correctly, says str can't be null
const getVerboseObject1 = createSelector(
    [getString],
    (str) => generateObject1(str)
);

// Errors correctly, says num can't be null
const getVerboseObject2 = createSelector(
    [getNumber],
    (num) => generateObject2(num)
);

// Errors correctly
const getVerboseComplexObjectTest1 = createSelector(
    [getObject1],
    (obj1) => generateComplexObject(obj1)
);

// Errors correctly
const getVerboseComplexObjectTest2 = createSelector(
    [
        getNumber,
        getObject1,
    ],
    (num, obj1) => generateComplexObject(num, obj1)
);

// Errors correctly
const getVerboseComplexObjectTest3 = createSelector(
    [
        getNumber,
        getObject1,
        getObject2,
    ],
    (num, obj1, obj2) => generateComplexObject(num, obj1, obj2)
);

// Errors correctly
const getVerboseComplexObjectTest4 = createSelector(
    [
        getObject1,
        getNumber,
        getObject2,
    ],
    (num, obj1, obj2) => generateComplexObject(num, obj1, obj2)
);

We did find a workaround for now that accomplishes what we want with avoiding the param duplication and it gives perfect error messages. But I am hoping that I can get help with why our expectations are wrong, or updated typings to support it.

workaround:

// This will error, but intentionally for the example situation above
const getSpreadComplexObject = createSelector(
    [
        getNumber,
        getObject1,
        getObject2,
    ],
    (...args) => generateComplexObject(...args)
);

Metadata

Metadata

Assignees

No one assigned

    Labels

    TypeScriptIssues related to TypeScript.

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions