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

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

Closed
yseymour opened this issue Sep 28, 2018 · 4 comments
Closed

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

yseymour opened this issue Sep 28, 2018 · 4 comments
Labels
Bug A bug in TypeScript
Milestone

Comments

@yseymour
Copy link

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.

@weswigham
Copy link
Member

weswigham commented Sep 28, 2018

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

@yseymour
Copy link
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.

@RyanCavanaugh RyanCavanaugh added the Bug A bug in TypeScript label Oct 1, 2018
@RyanCavanaugh RyanCavanaugh added this to the Future milestone Oct 1, 2018
pikax pushed a commit to pikax/vuex that referenced this issue Feb 14, 2019
@fabb fabb mentioned this issue Jan 1, 2020
@fabb
Copy link

fabb commented Jan 1, 2020

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

@yseymour
Copy link
Author

yseymour commented Jun 6, 2020

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

@yseymour yseymour closed this as completed Jun 6, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Bug A bug in TypeScript
Projects
None yet
Development

No branches or pull requests

4 participants