Skip to content

Indexing a mapped type with a nested key from the original type emits compiler errors #43982

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
00benallen opened this issue May 6, 2021 · 3 comments · Fixed by #52069
Closed
Labels
Design Limitation Constraints of the existing architecture prevent this from being fixed

Comments

@00benallen
Copy link

Bug Report / Feature Request

When indexing a mapped type that is meant to have the same keys as an original type, the compiler emits errors when using a generic type which is a key of the original type:

Playground Link with Demonstration

https://www.typescriptlang.org/play?module=5&strict=true&ts=4.0.5&ssl=1&ssc=1&pln=43&pc=1#code/KYDwDg9gTgLgBASwHY2FAZgQwMbDgeSgQHNlMAbOAbwCg564woIwBGALmroZ4GcBXAEYAFZm068YRJMQA03HvQEixAJglTkxBXAC+8nkxbquihstEsAzBulydfIZbAAWW1p26aX0JFhwYAE8wPABpYED8dEISMkoAXjgAawiIdAIiUiQKAG4aX2h4IJC4ADlgSWAAE3DI6My4gDFoAB4AFThQVCQq3jhaqJisigA+OESUwLSM2OzyAG02gF0afPBCuGwIJEk4YmAYAGVNGUbmAFshuPG4FtDOkG7e-ojBhrnZMoenvvLKmte9VmFGaUDuIxGAApoMDyJwrh9khFOKFPkgKqgAYFOKUAJTw94UeahJbzUpLUwMKAHfhQJBwGHDBaTUno-61JZ5LxrPxFYJ4Q6Yc7AWq8dpjRK0HjExD0ybTZacKVmODE1SypFTdJtYlLJacJD8c6CNB5HhebkFfzFPAAWUwYBCVTOEEuhIScEFwtFLQRozyNAA9AAqYN0YNwNoACwQfWFMCjECqcF4if45GTAHdoElPpg+vdMD0vphqXt+KWi6hqgEIHATXAAG4UBDJyZ9aaCCAJmZMuBF5P2x3VF1u2HhuAT6N4TOYQIBKN4G19Ut4Xg0sCfBDwWOy5vkVu1zbbSRQfjYeBF2WSIu4BnpIdO0d+yiZmPYKNwKP575QTCMMQ0BgBAKgZKBEDjWNeC0fsAJYIDAgnKcYz6UAhTAcg8AoXg62zKAkj6BB0gASTgKptgAcngfh1zgNlMU1XhPgAKxo+BtjwTDG2AShpkmOBzgdMAYN3dBkGAAA6OB1S4niO3SfjBMdGDBGpTACKk8NAx5DYth2eB9iOE5iAAQR6UojRNKBn3dMyqkfGtEh0O5vmAHoC0BF9PlKVz3LKDFqgGIEmVBcEoQcBhGTiAlYU+CL6CUp8LhfTgHOdZL3TilVNRRNEAqxHEdHxVUX11Mklk+NKbNhMryQpeIxioOBA0DSN+TgCjSgozYiyQbt6zwGiaxgOtkCqUAAnaiivRFCIxRfEZdQo+K4GpGBaXpeYormeYWXmejAoiCqBKEkcMpqvaDqxPVmtatopq6nqkD6+AGyG5MRtlcaQEmkppqFWbAnm91FpJZbzVWGggA

Static Code Sample

export interface Original {
    prop1: {
        subProp1: string,
        subProp2: string
    },
    prop2: {
        subProp3: string,
        subProp4: string
    }
}

export type KeyOfOriginal = keyof Original;
export type NestedKeyOfOriginalFor<T extends KeyOfOriginal> = keyof Original[T]

export const getStringFromOriginal = <K extends KeyOfOriginal, N extends NestedKeyOfOriginalFor<K>>(original: Original, key: K, nestedKey: N): Original[K][N] {
    return original[key][nestedKey];
}

export type SameKeys<T> = {
    [K in keyof T]: {
        [K2 in keyof T[K]]: number;
    }
}

export type MappedFromOriginal = SameKeys<Original>;

/**
* This method should work, as K and N are guaranteed to be valid keys of both Original and MappedFromOriginal
*
 * The way the types are setup, it is invalid to construct an instance of MappedFromOriginal which has extra properties or is missing a property
*
 * This example also works if I don't use nested keys, just one level of key mapping is fine. 2 levels of key mapping breaks.
 */

export const getStringAndNumberFromOriginalAndMapped =
    <K extends KeyOfOriginal, N extends NestedKeyOfOriginalFor<K>>(
        original: Original,
        mappedFromOriginal: MappedFromOriginal,
        key: K, nestedKey: N
    ): [Original[K][N], MappedFromOriginal[K][N]] => { // Type 'N' cannot be used to index type 'SameKeys<Original>[K]'
        return [original[key][nestedKey], mappedFromOriginal[key][nestedKey]] // Type 'N' cannot be used to index type 'SameKeys<Original>[K]'
    }
@RyanCavanaugh RyanCavanaugh added the Design Limitation Constraints of the existing architecture prevent this from being fixed label May 6, 2021
@RyanCavanaugh
Copy link
Member

TS doesn't understand that, at a higher level, these keys are actually the same. You can write this for the desired behavior as

export const getStringAndNumberFromOriginalAndMapped =
    <K extends KeyOfOriginal & keyof MappedFromOriginal, N extends NestedKeyOfOriginalFor<K> & keyof MappedFromOriginal[keyof MappedFromOriginal]>(
        original: Original,
        mappedFromOriginal: MappedFromOriginal,
        key: K, nestedKey: N
    ): [Original[K][N], MappedFromOriginal[K][N]] => { // Type 'N' cannot be used to index type 'SameKeys<Original>[K]'
        return [original[key][nestedKey], mappedFromOriginal[key][nestedKey]] // Type 'N' cannot be used to index type 'SameKeys<Original>[K]'
    }

@00benallen
Copy link
Author

@RyanCavanaugh I'll try this when I get into work tomorrow, but I guess the question is, COULD TS understand this without being explicitly told?

@RyanCavanaugh
Copy link
Member

Maybe? I doubt anything is truly impossible. Types like this are relatively rare and can be written in proposed form without loss of generality, so it doesn't seem worth it to add a concept of "same keys but different property types" to the language to accommodate this. I'm not even 100% convinced the given function is truly sound without the added intersections.

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

Successfully merging a pull request may close this issue.

2 participants