Skip to content

unknown[] as rest params with generic parameter breaks generic inference #27439

Description

TypeScript Version: 3.1.0

Search Terms: unknown conditional rest params

Code

declare function f<T>(x: T): T;
declare function g<T>(x: T): number;

//shortened builtin: type ReturnType<F> = F extends (...args: any[]) => infer U ? U: any
type ReturnOf<F> = F extends (...args: unknown[]) => infer U ? U : never

declare const a: ReturnType<typeof g>; // number
declare const b: ReturnOf<typeof g>; // number
declare const c: ReturnType<typeof f>; // {}
declare const d: ReturnOf<typeof f>; // never

Expected behavior: d is the same as c (i.e. type {})

Actual behavior: d is type never

Playground Link

Related Issues: didn't see any; apologies if I missed it

Notes: this only happens if there's a T-typed (or union with T) parameter. Even something like Promise<T> seems to cause it to evaluate to {}. I'm not even 100% sure this is a bug (I might be missing/misunderstanding something in how unknown is handled), but it seems like in this situation it should behave the same as the any version.

Activity

  1. DanielRosenwasser commented on Sep 28, 2018

    @DanielRosenwasser
    Member

    This is due to two things:

    1. T has an implicit constraint of {} which is a subtype of unknown.
    2. Since parameters are contravariantly related, unknown[] is a problematic type for args in ReturnOf. This is because unknown isn't assignable to {} (remember, contravariance means we switch directions when relating two types).

    You'd be best off picking the lowest bound for the type of args by using never:

    type ReturnOf<F> = F extends (...args: never[]) => infer U ? U : never;

    If #26796 were solved, this would've worked, but I still believe never is the best type here.

  2. jack-williams commented on Sep 28, 2018

    @jack-williams
    Collaborator

    EDIT: Sorry I have successfully managed to confuse myself, feel free to ignore my question.

    Daniel Rosenwasser (@DanielRosenwasser) So functions are contravariant in the the domain when checking conditional types, even with strictFunctionTypes off?

    // --strictFunctionTypes: off
    declare function f<T>(x: T): T;
    type ReturnOf<F> = F extends (...args: unknown[]) => infer U ? U : never;
    type Test<F> = F extends (...args: unknown[]) => unknown ? unknown : never;
    
    declare const d: ReturnOf<typeof f>; // never;
    declare const e: Test<typeof f>; // unknown;

    What I think is happening is that first U is inferred from the base constraint of T, so is {}, it then tries to unify <T>(x: T): T and the inferred type (...args: unknown[]) => {}, which actually instantiates T to be unknown by using the domain, so you get an error on the return type. I think this is why the behavior is independent of strict function checks.

  3. DanielRosenwasser commented on Sep 29, 2018

    @DanielRosenwasser
    Member

    Hmm, I want to say you're right, but I'd like to check with Anders Hejlsberg (@ahejlsberg) or Wesley Wigham (@weswigham) first.

  4. ahejlsberg commented on Sep 29, 2018

    @ahejlsberg
    Member
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Design LimitationConstraints of the existing architecture prevent this from being fixed

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions