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

Generics inside array doesn't determined correctly #47393

Closed
Flairax opened this issue Jan 11, 2022 · 5 comments
Closed

Generics inside array doesn't determined correctly #47393

Flairax opened this issue Jan 11, 2022 · 5 comments
Labels
Design Limitation Constraints of the existing architecture prevent this from being fixed

Comments

@Flairax
Copy link

Flairax commented Jan 11, 2022

Bug Report

🔎 Search Terms

Generics inside array doesn't determined correctly

🕗 Version & Regression Information

  • This changed between versions 3.8.3 and 3.9.7 - types inferred correctly if function argument is omited.
  • If the argument is present not working in any version.

⏯ Playground Link

Playground link with relevant code

💻 Code

interface IPipe<I, O> { }
interface Pipeline<I, O> { }

class Bus<T> {
    pipe<A>(pipes: [IPipe<T, A>]): Pipeline<T, A>;
    pipe<A, B>(pipes: [IPipe<T, A>, IPipe<A, B>]): Pipeline<T, B>;
    pipe<A, B, C>(pipes: [IPipe<T, A>, IPipe<A, B>, IPipe<B, C>]): Pipeline<T, C>;
    pipe<A, B, C, D>(pipes: [IPipe<T, A>, IPipe<A, B>, IPipe<B, C>, IPipe<C, D>]): Pipeline<T, D>;
    pipe(pipes: IPipe<any, any>[]): Pipeline<any, any> {
        return null;
    }
}

function map<I, O>(mapper: (value: I) => O): IPipe<I, O> { return; }

const bus = new Bus<number>();

bus.pipe([
    map(() => ``),
    map(() => [true]),
    map(() => [``]),
    map(() => false)
]);

🙁 Actual behavior

Every next pipe first generic argument determined as Bus basic type.

bus.pipe(
   map((x) => ``),
     // ^ x: number
   map((x) => [true]),
     // ^ x: number
   map((x) => [``]),
     // ^ x: number
   map((x) => false)
     // ^ x: number
);

🙂 Expected behavior

Every next pipe first generic argument should be based on previous pipe second generic type.

bus.pipe(
   map((x) => ``),
     // ^ x: number
   map((x) => [true]),
     // ^ x: string
   map((x) => [``]),
     // ^ x: boolean[]
   map((x) => false)
     // ^ x: string[]
);
@whzx5byb
Copy link

  • This changed between versions 3.8.3 and 3.9.7

Actually your example doesn't work in 3.8.3. Add the omitted first argument in the functions like bus.pipe(map((x) => ``), map((x) => [true]), map((x) => [``]), map((x) => false)); and these x will still be inferred as number.

And I would suggest using variadic arguments as workaround.

interface IPipe<I, O> { }
interface Pipeline<I, O> { }

declare class Bus<T> {
    pipe<A>(...pipes: [IPipe<T, A>]): Pipeline<T, A>;
    pipe<A, B>(...pipes: [IPipe<T, A>, IPipe<A, B>]): Pipeline<T, B>;
    pipe<A, B, C>(...pipes: [IPipe<T, A>, IPipe<A, B>, IPipe<B, C>]): Pipeline<T, C>;
    pipe<A, B, C, D>(...pipes: [IPipe<T, A>, IPipe<A, B>, IPipe<B, C>, IPipe<C, D>]): Pipeline<T, D>;
}

declare function map<I, O>(mapper: (value: I) => O): IPipe<I, O>

const bus = new Bus<number>();

bus.pipe(
    map((x) => ``),
      // ^ x: number
    map((x) => [true]),
      // ^ x: string
    map((x) => [``]),
      // ^ x: boolean[]
    map((x) => false)
      // ^ x: string[]
);

Playground

@Flairax
Copy link
Author

Flairax commented Jan 12, 2022

@whzx5byb yes, you are right, doesn't seems to ever worked. Unfortunately variadic arguments is not an option because in my code I want to use few more arguments after pipes.

interface IConfig<I, O> {}

class Bus<T> {
   pipe<A>(pipes: [IPipe<T, A>], config?: IConfig<T, A>): Pipeline<T, A>;
   pipe<A, B>(pipes: [IPipe<T, A>, IPipe<A, B>], config?: IConfig<T, B>): Pipeline<T, B>;
   pipe<A, B, C>(pipes: [IPipe<T, A>, IPipe<A, B>, IPipe<B, C>], config?: IConfig<T, C>): Pipeline<T, C>;
   pipe<A, B, C, D>(pipes: [IPipe<T, A>, IPipe<A, B>, IPipe<B, C>, IPipe<C, D>], config?: IConfig<T, D>): Pipeline<T, D>;
   pipe(pipes: IPipe<any, any>[]): Pipeline<any, any> {
      return null;
   }
}

Because of this such solution is also not fitting

class Bus<T> {
   pipe<A>(pipe_1: IPipe<T, A>): Pipeline<T, A>;
   pipe<A, B>(pipe_1: IPipe<T, A>, pipe_2: IPipe<A, B>): Pipeline<T, B>;
   pipe<A, B, C>(pipe_1: IPipe<T, A>, pipe_2: IPipe<A, B>, pipe_3: IPipe<B, C>): Pipeline<T, C>;
   pipe<A, B, C, D>(pipe_1: IPipe<T, A>, pipe_2: IPipe<A, B>, pipe_3: IPipe<B, C>, pipe_4: IPipe<C, D>): Pipeline<T, D>;
   pipe(pipes: IPipe<any, any>[]): Pipeline<any, any> {
      return null;
   }
}

Thanks anyway :)

@RyanCavanaugh RyanCavanaugh added the Needs More Info The issue still hasn't been fully clarified label Jan 12, 2022
@RyanCavanaugh
Copy link
Member

I don't understand what the actual vs expected problem is. Can you state it more clearly?

@Flairax
Copy link
Author

Flairax commented Jan 12, 2022

@RyanCavanaugh, changed description in @whzx5byb way. Think it more clearer now.

@RyanCavanaugh RyanCavanaugh added Design Limitation Constraints of the existing architecture prevent this from being fixed and removed Needs More Info The issue still hasn't been fully clarified labels Jan 12, 2022
@RyanCavanaugh
Copy link
Member

Thanks, that's much clearer.

Generic inference has a fixed number of passes for fixing type parameters and isn't capable of inferring this sort of pattern.

See also #30134

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Design Limitation Constraints of the existing architecture prevent this from being fixed
Projects
None yet
Development

No branches or pull requests

3 participants