Skip to content

Second infer overwrites initally infered type defintion #32389

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

Closed
clentfort opened this issue Jul 13, 2019 · 5 comments
Closed

Second infer overwrites initally infered type defintion #32389

clentfort opened this issue Jul 13, 2019 · 5 comments
Labels
Design Limitation Constraints of the existing architecture prevent this from being fixed

Comments

@clentfort
Copy link

TypeScript Version: 3.5.1

Search Terms:

Infer, Multiple, Generic

Code

type RequesterBaseType = (...args: any[]) => any;

type SuccessUnpacker<RequesterType extends RequesterBaseType, PayloadType> = (
  payload: ReturnType<RequesterType>
) => PayloadType;


type ResultTracker<PayloadType> = PayloadType extends ArrayLike<infer PackedPayloadType>
  ? (instance: PackedPayloadType) => Partial<PackedPayloadType>
  : (instance: PayloadType) => Partial<PayloadType>;

declare function createApiModule<
  PayloadType,
  TrackingType,
  RequesterType extends RequesterBaseType = RequesterBaseType
>(
  apiMethod: RequesterType,
  unpackSuccess: SuccessUnpacker<RequesterType, PayloadType>,
  trackResult?: ResultTracker<PayloadType>
): void;

type MyPayload = {
    a: string;
}
createApiModule(
   () => [{a: 'a'}] as MyPayload[],
  payload => payload,
  ({ a }) => ({ a })
);

Expected behavior:

Allowed return type of unpackSuccess should be as MyPayload[]

Actual behavior:

Allowed return type of unpackSuccess is be MyPayload.

Playground Link

Related Issues:


I tried to search for related issues but could not find anything, maybe I don't know the correct terminology.

@clentfort
Copy link
Author

Here is a more minimal example

type Infer1<T> = () => T;
type Infer2<T> = T extends ArrayLike<infer S>
  ? (a: S) => Partial<S>
  : (a: T) => Partial<T>;

declare function f<T>(infer1: Infer1<T>, infer2: Infer2<T>): void;
declare function f1(): Array<{ a: any }>;

f(f1, a => a);

I expect f1 to be a valid input to f but TypeScript complains about it.

http://www.typescriptlang.org/play/#code/C4TwDgpgBAkgdgMwgJwIwB4AqA+KBeKACgEp9dMBuAKFElkRQCYtcDMoIAPYCOAEwDOUAILJkAQxAAZAJYBrCOhkNkUAMrYqUKAH4i4gFzrSeXAAVxyYDPEAbdBq1QjhQ1Ewnzl63ZbUqfBAAxraW0AgArnBB1gD2cFAILITKSGhG8GkYOAA0UKlMGSrMOMRGAG6xMnzUgSFhiVExMvGJqCRGohIg6ADeUG7icCBQAL7Y-giECKh54mQDxNRAA

Interestingly this modified example, where Infer2 does not return a function, works as expected http://www.typescriptlang.org/play/#code/C4TwDgpgBAkgdgMwgJwIwB4AqA+KBeKACgEp9dMBuAKFElkRQCYtcDMoIAPYCOAEwDOUAILJkAQxAAZAJYBrCOhkNkUAMrYqUKAH51WqAC4olKlT4QAxgBtxyaAgCucS8BkB7OFAQtCypGjG8AEYOAA0UP5MQSrMOMTGAG7uMnzUFjZ2Ds6uHl4IqCTGohIg6ADeUOLG4nAgUAC+2NRUCIQFEeXVUKgNxNRAA

@clentfort
Copy link
Author

I found a workaround that at least for now solves my problem by changing Infer2 to the following

type Infer2<T> = T extends ArrayLike<infer S>
  ? <SS extends S>(s: SS) => Partial<SS>
  : <TT extends T>(t: TT) => Partial<TT>;

I'm not sure if this could have undesired side-effects, but at least it allows me to continue.

@dragomirtitian
Copy link
Contributor

dragomirtitian commented Jul 16, 2019

@clentfort If you want a workaround using & {} will decrease the priority of the second inference site:

type Infer1<T> = () => T;
type Infer2<T> = T extends ArrayLike<infer S>
  ? (a: S) => Partial<S>
  : (a: T) => Partial<T>;

declare function f<T>(infer1: Infer1<T>, infer2: Infer2<T & {}>): void;
declare function f1(): Array<{ a: any }>;

f(f1, a => a);

link

Your workaround makes the second parameter a generic function, which will behave differently. For example this would not be legal:

type Infer1<T> = () => T;
type Infer2<T> = T extends ArrayLike<infer S>
  ? <SS extends S>(s: SS) => Partial<SS>
  : <TT extends T>(t: TT) => Partial<TT>;

declare function f<T>(infer1: Infer1<T>, infer2: Infer2<T>): void;
declare function f1(): { a: any };

f(f1, (s) => ({ a: 1 })); // concrete value assigned to a generic type not allowed.

@clentfort
Copy link
Author

Thanks for pointing this out, this means my workaround is exactly what I want. :)

@RyanCavanaugh
Copy link
Member

See #30134 - the particular use of depending on an infer in a conditional type simultaneously while doing an outer generic inference isn't well-supported in the current algorithm.

@RyanCavanaugh RyanCavanaugh added the Design Limitation Constraints of the existing architecture prevent this from being fixed label Jul 31, 2019
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