Skip to content

regression in 3.1+: mapped type cannot be used to index type #27413

Closed

Description

TypeScript Version: 3.1.1 and 3.2.0-dev.20180927

Search Terms:
mapped type index

Code

// Given:
//   T = {foo: number}
// The return type of T.select({bar: 'foo'}) should be
//   {bar: number}

interface Table<T> {
	select<U extends {[K: string]: keyof T}>
	      (fields: U): {[K in keyof U]: T[U[K]]}
}

Expected behavior:
U[K] is keyof T and so T[U[K]] is computed successfully

Actual behavior:
Gives the error "Type 'U[K]' cannot be used to index type 'T'".

The same code compiles with tsc 3.0.3 and the return type gets computed correctly.

Activity

  1. weswigham commented on Sep 28, 2018

    @weswigham
    Member
    interface Table<T> {
    	select<U extends {[K: string]: keyof T}>
    	      (fields: U): {[K in keyof U]: T[U[K]]}
    }
    
    declare var t: Table<{x: string}>;
    declare const s: unique symbol;
    // your definition of `U` allows symbolic keys of `U` to have non-`keyof T` types,
    // which in turn means a call like this would typecheck. `T` indexed by one of these
    // types wouldn't exist, which is why we issue this error
    const result = t.select({[s]: "not a key", member: "x"});
    // `result` above will end up of type `{[s]: unknown, member: string}`, since your type doesn't
    // actually handle the `symbol` key case

    I assume, anyway. I actually tried what I think would be the correct version that fixes that, though:

    interface Table<T> {
    	select<U extends {[K: string]: keyof T}>
    	      (fields: U): {[K in Extract<keyof U, string>]: T[U[K]]}
    }

    and had the same issue; so something else must be the issue.

    In any case, you can work around the problem by writing your type like so:

    interface Table<T> {
    	select<U extends {[K: string]: keyof T}>
    	      (fields: U): {[K in Extract<keyof U, string>]: T[U[K] & keyof T]}
    }

    (I left in the Extract so symbols on the input type get ignored, and the & keyof T in the index is what makes it typecheck despite the issue. It actually should have absolutely no effect on the resulting types when the generics are instantiated; but it shouldn't be necessary since the constraint of U[K] when K extends Extract<keyof U, string> aught to be keyof T as you implied.)

  2. yseymour commented on Sep 28, 2018

    @yseymour
    Author

    Thanks, good call on the workaround. U[K] used to be implicitly constrained to keyof T. I'm not sure exactly when the behaviour changed.

  3. fabb commented on Jan 1, 2020

    @fabb

    Maybe related:

    function getValues<T, K extends readonly (keyof T)[]>(object: T, keys: K): { [P in keyof K]: T[K[P]] } {
        return keys.map(key => object[key]) as any
    }
    

    This shows the error: Type 'K[P]' cannot be used to index type 'T'.

  4. yseymour commented on Jun 6, 2020

    @yseymour
    Author

    Closing this since it looks like the original issue is fixed.

  5. 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

Assignees

No one assigned

    Labels

    BugA bug in TypeScript

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions