Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Incorrect overload is used for pipe when first function has optional parameter #29913

Closed
OliverJAsh opened this issue Feb 14, 2019 · 4 comments
Closed
Labels
Working as Intended The behavior described is the intended behavior; this is not a bug

Comments

@OliverJAsh
Copy link
Contributor

OliverJAsh commented Feb 14, 2019

TypeScript Version: 3.3.1

Search Terms:

Code

  • when strictFunctionTypes is disabled
  • when first composed function parameter is optional
  • when first pipe overload is zero parameters for first function
declare const pipe: {
    // 0-argument first function
    // Workaround: disable this overload
    <A>(a: () => A): () => A;

    // 1-argument first function
    <A, B>(ab: (a: A) => B): (a: A) => B;
};

// Expected type: `(a: {} | undefined) => number`
// Actual type: `() => number`
const fn = pipe((_a?: {}) => 1);

Related Issues:
#29904

@RyanCavanaugh
Copy link
Member

The first overload that matches is always chosen. The pipe overloads should be in the other order.

@RyanCavanaugh RyanCavanaugh added the Working as Intended The behavior described is the intended behavior; this is not a bug label Feb 27, 2019
@OliverJAsh
Copy link
Contributor Author

How come this behaviour only applies when strictFunctionTypes is disabled?

This is the order of overloads used by Lodash's flow (aka pipe) function. I have raised a PR to fix the overload order, as you suggested: DefinitelyTyped/DefinitelyTyped#33450.

@RyanCavanaugh
Copy link
Member

strictFunctionTypes off means that parameter types are compared bivariantly, so the {} | undefined being assignable from the implicit no-argument undefined is acceptable.

@OliverJAsh
Copy link
Contributor Author

The first overload that matches is always chosen. The pipe overloads should be in the other order.

If we do this, it fixes the example above, but it causes other issues:

{
    declare const pipe: {
        // 1-argument first function
        <A, B>(ab: (a: A) => B): (a: A) => B;

        // 0-argument first function
        // Workaround: disable this overload
        <A>(a: () => A): () => A;
    };

    // Expected and actual type: `(a: {} | undefined) => number`
    const fn1 = pipe((_a?: {}) => 1);

    // Expected type: () => number
    // Actual type: (a: unknown) => number
    const fn2 = pipe(() => 1);
}

Workaround: use generic rest parameters:

{
    declare const pipe: {
        <A extends any[], B>(ab: (...a: A) => B): (...a: A) => B;
    };

    // Expected and actual type: `(a: {} | undefined) => number`
    const fn1 = pipe((_a?: {}) => 1);

    // Expected and actual type: () => number
    const fn2 = pipe(() => 1);
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Working as Intended The behavior described is the intended behavior; this is not a bug
Projects
None yet
Development

No branches or pull requests

2 participants