-
Notifications
You must be signed in to change notification settings - Fork 12.8k
Parameter of a generic interface doesn't work when it's a generic index of an another interface #32365
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
Comments
I think this is a design limitation. The issue is that you end up in a situation where you want to relate: SettingsTypes[T] <: { [K in keyof SettingsTypes[T]]?: string } To make any progress you need to simplify the left hand side using the constraint of There might be some way to get this through by using some intermediate (conditional) types, but convincing the checker a priori seems hard - the level of reasoning is non-trivial. Am I right in thinking that all you really care about is saying that |
Hi, thanks for you response. Actually the Params part comes from an external library. See RouteComponentProps interface. However now I think I made an XY problem situation by overcomplicating things. What I'm trying to do is I'm experimenting in my app to get named routes with typed parameters. And in fact it works great! I have named routes: const PAYMENTS_LIST = '/payments';
const PAYMENT_DETAILS = '/payment/:id'; I also made a single interface for all routes parameters: interface RouteParameters {
[PAYMENT_DETAILS]: {
id: string;
};
} Finally I wrapped the interface MakePathFunction {
<T extends keyof RouteParameters >(route: T, params: RouteParameters[T]): string;
(route: string): string;
}
export const makePath: MakePathFunction = <T>(route, params = {}) => {
return formatRoute(route, params);
}; This works like a charm. The problem is that when I'm creating a React component with route parameters I should use the const Payment: FC<RouteComponentProps<{id: string}>> To avoid typing duplication I wanted to reuse the const Payment: FC<RouteComponentProps<RouteParameters[typeof PAYMENT_DETAILS]>> It looks messy so I tried to shorter the typing with an alias: type RCP<T extends keyof RouteParameters> = RouteComponentProps<RouteParameters[T]>;
const Payment: FC<RCP<typeof PAYMENT_DETAILS>> And finally here the compiler was complaining about the export interface PaymentDetailsRoute {
id: string;
}
export interface RouteParameters {
[PAYMENT_DETAILS]: PaymentDetailsRoute;
}
const Payment: FC<RouteComponentProps<PaymentDetailsRoute>> = (props) => { I think the issue may be closed now. Thanks for explanation. PS: above code is not copied 1:1 from project so it may have typos. |
@jack-williams Maybe I'm missing something but: SettingsTypes[T] <: { [K in keyof SettingsTypes[T]]?: string } Why can't you substitute |
Looking into this abit more, I believe your issue boils down to this: type SettingsTypesKey<K extends "volume" | "resulution"> = K;
type Broken<T extends keyof SettingsTypes> = SettingsTypesKey<keyof SettingsTypes[T]>;
// Type 'string | number | symbol' does not satisfy the constraint '"volume" | "resulution"'. Basically
per the comment: // A source type T is related to a target type { [P in Q]?: X }
// if some constituent Q' of Q is related to keyof T and T[Q'] is related to X. but there are no index signatures on Expanding type IsVolume<K extends "volume"> = K;
type Broken<T extends keyof SettingsTypes> = IsVolume<keyof SettingsTypes[T]>;
// If we are allowed to expand T we get:
// 1. IsVolume<keyof SettingsTypes[T]>
// 2. IsVolume<keyof SettingsTypes["audio" | "video"]>
// 3. IsVolume<keyof SettingsTypes["audio"] | SettingsTypes["video"]>
// 4. IsVolume<"volume" & "resulution"> --> this would be ok
// Yet clearly this is unsound
type Res = Broken<"video">; // resolution So I would leave this issue open and let someone on the team have a definitive look. I'm glad you have something that works for you though! |
Minimal version: interface SettingsTypes {
audio: {
volume: string;
};
video: {
resolution: string;
}
}
type SettingsTypesKey<K extends "volume" | "resolution"> = K;
type Broken<T extends keyof SettingsTypes> = SettingsTypesKey<keyof SettingsTypes[T]>; Note that this version works: interface SettingsTypes {
audio: {
volume: string;
};
video: {
resolution: string;
}
}
type SettingsTypesKey<K> = K extends "volume" | "resolution" ? K : never;
type Broken<T extends keyof SettingsTypes> = SettingsTypesKey<keyof SettingsTypes[T]>; |
TypeScript Version: typescript@next, [email protected]
Search Terms:
generic index is not assignable
Code
Expected behavior:
Generic should work the same if we pass an specific index of an interface (1), a generic index of an interface (2) or when we pass the type directly (3)
Actual behavior:
Typescript throws an error only for the construction
Settings<SettingsTypes[T]>
.Playground Link:
https://www.typescriptlang.org/play/#code/JYOwLgpgTgZghgYwgAgMoTGUBzAzgFQE8AHCXZAbwChlk4BXAE2AHsAuSm25ANxYBt6AWwgdcYKDgDcXAL4zaPYIwjtO3ZFDL1BWFiDETpcqrKpVQkWIhTpMOXAB4ACnChwh5CAA9IIRuQUyADaANLIoMgA1hCELDDIru6eALoA-IaSINjIsgB86sgI+jDA2BxJHrgy8uYA9HXIAKLeHsT8KACMVGAkKPgAFsC4AOosUFHkALxoGFjZTnbzeESkuMEA5AzMLBspeTJUDc2tQu0oAEw9fciDwwAiLGTgYxOO+Mg+fgHRsfGz9gWqzIBRmSwcjnBQL663w+0OxyaACUkQB5JFsI6NYHIDZBPiCESZaS5DYRcggFhgOi4XBlEBwABGHWQYBYrJueJC4UiMTiCShKxhwTh6WJ2WQAB9kPR-BBSiAIIwpKSAHT1RosAYAGmQLCiyEZ9GpYAGEBAyAA7gNCKyhuRLeNJmkNSc2iyAMzXUi3e2vSYAQWwcEiYLmEPxAmEomQ4iy2BV+SkQA
Related Issues:
32017 and 31904
The text was updated successfully, but these errors were encountered: