Skip to content

Inconsistent contextual typing for functions between object literals and tuples #40809

Closed

Description

TypeScript Version: 4.1.0-dev.20200928

Search Terms: contextual type, function, generic, mapped type, inference, tuple, implicit any, noImplicitAny

Code

type Mapped<T> = {
    [K in keyof T]: [T[K], (arg: T) => boolean];
}
function inferMapped<T>(arg: Mapped<T>): void {}
inferMapped({
    key: [3, (arg) => arg.key > 5],
})

type Mapped2<T> = {
    [K in keyof T]: [T[K], unknown extends T ? unknown : (arg: T) => boolean];
}
function inferMapped2<T>(arg: Mapped2<T>): void {}
inferMapped2({
    key: [3, (arg) => arg.key > 5],
})

type MappedReadonly<T> = {
    readonly [K in keyof T]: readonly [T[K], (arg: T) => boolean];
}
function inferMappedReadonly<T>(arg: MappedReadonly<T>): void {}
inferMappedReadonly({
    key: [3, (arg) => arg.key > 5],
} as const)

type MappedObj<T> = {
    [K in keyof T]: { 0: T[K], 1: (arg: T) => boolean };
}
function inferMappedObj<T>(arg: MappedObj<T>): void {}
inferMappedObj({
    key: { 0: 3, 1: (arg) => arg.key > 5 },
})

Expected behavior:
At least one of (inferMapped, inferMapped2, inferMappedReadonly) should provide the same contextual type for the tuple's second element (the arrow function) as inferMappedObj provides
Actual behavior:

  • inferMapped completely fails to infer T (annoying but not entirely unreasonable)
  • inferMapped2 and inferMappedReadonly correctly infer T, and show the correct type for key ([number, (arg: { key: number; }) => boolean]) as a tooltip in Playground, but the arrow function's parameter is treated as an implicit any even though its type is known
  • inferMappedObj works perfectly; the arrow function's arg is correctly contextually typed as { key: number; }

Playground Link: here
Related Issues: I got the idea to use mapped type inference here from a Vue typings issue, but I can't find it now - the solution there involved return type annotations to avoid problems from circularity, but nothing is inferred based on return types here, and the annotations don't help anyway

Activity

  1. RyanCavanaugh commented on Sep 28, 2020

    @RyanCavanaugh
    Member

    Interesting, I expected some as consts to fix these but nothing much seemed to work

  2. artemiswkearney commented on Sep 28, 2020

    @artemiswkearney
    Author

    I'd rather not need an as const at every call site to get inference for the real function this is based on, but even that would be better than not getting the contextual types at all.
    I'd also be happy with a way to mark the parameter as a const inference site or something at the declaration end; there's probably already an issue about that somewhere?

  3. artemiswkearney commented on Oct 4, 2020

    @artemiswkearney
    Author

    Playing around some more, and it seems like as const just obliterates contextual types for functions in general?
    playground
    Probably deserves its own issue? This really limits the usefulness of as const in any context involving functions.

  4. ahejlsberg commented on Oct 14, 2020

    @ahejlsberg
    Member

    I agree it's inconsistent that inference succeeds with an object literal but fails with an equivalent tuple type. It appears the fix is as simple as adding an additional check for tuple types in isPartiallyInferableType, all of the other logic is already there. I will put up a PR.

  5. added
    BugA bug in TypeScript
    and removed
    Needs InvestigationThis issue needs a team member to investigate its status.
    on Oct 14, 2020
  6. locked as resolved and limited conversation to collaborators on Oct 21, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

BugA bug in TypeScriptFix AvailableA PR has been opened for this issue

Type

No type

Projects

No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions