Skip to content

Generic type is not correctly inferred from the function return type when it is a function expression type #52114

@markostanimirovic

Description

@markostanimirovic

Bug Report

🕗 Version & Regression Information

Tested with different TS v4 versions. It does not seem like a regression.

⏯ Playground Link

Playground link with relevant code

💻 Code

// === Reproduction ===

export type ActionReducer<State> = (state: State | undefined) => State;

export function createReducer<State>(initialState: State): ActionReducer<State> {
    return {} as any;
}

export function createFeature<State>(config: {
    reducer: ActionReducer<State>,
    selectors: (state: State) => unknown,
}) {}

createFeature({
    reducer: createReducer(''),
    // the `state` type is `unknown`
    // if we remove `state` argument the error will disappear
    selectors: (state) => ({}),
    // selectors: () => ({})
});

// === Workarounds ===

// === Workaround 1:
// Define return type as another generic

export function createReducer2<
  State,
  Reducer extends ActionReducer<State> = ActionReducer<State>
>(initialState: State): Reducer {
    return {} as any;
}

createFeature({
    reducer: createReducer2(123),
    // the `state` type is correctly inferred (`number` in this case)
    selectors: (state) => ({})
});

More workarounds are available at the playground link.

🙁 Actual behavior

A generic type is not correctly inferred from the function return type when it is a function expression type.

By the way, I noticed that the State type will be correctly inferred when the ActionReducer from the example above is not defined as a function expression type:

// `ActionReducer` is not a function type anymore
export type ActionReducer<T> = T | undefined;

export function createReducer<T>(initialState: T): T {
    return {} as any;
}

export function createFeature<State>(config: {
    reducer: ActionReducer<State>,
    selectors: (state: State) => unknown,
}) {}

createFeature({
    reducer: createReducer(''),
    // the `state` type is correctly inferred
    selectors: (state) => ({}),
});

🙂 Expected behavior

I'd expect the same behavior when ActionReducer is typed as a function expression.

Metadata

Metadata

Assignees

No one assigned

    Labels

    DuplicateAn existing issue was already created

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions