In TypeScript, I created custom types using template literal types to dynamically type the getters and setters of fields. The types are structured like this (Playground Link):
export type Getters<T> = {
[K in `get${Capitalize<keyof T & string>}`]: K extends `get${infer S}` ? (
S extends keyof T ? () => T[S] : (
Uncapitalize<S> extends keyof T ? () => T[Uncapitalize<S>] : never
)
) : never;
};
export type Setters<T> = {
[K in `set${Capitalize<keyof T & string>}`]: K extends `set${infer S}` ? (
S extends keyof T ? (newValue: T[S]) => void : (
Uncapitalize<S> extends keyof T ? (newValue: T[Uncapitalize<S>]) => void : never
// ^^^^^^^^^^^^^^^^^^
)
) : never;
};
However, I encountered a type error on line 12 (highlighted above), within the Setters<T>
type definition. The error states:
Type 'Uncapitalize' cannot be used to index type 'T'.
Despite checking that
Uncapitalize<S> extends keyof T
just before, I am puzzled as to why there is still a type error. I am also wondering why it worked without errors a few lines above, in the Getters<T>
type definition. This leads me to question the difference between them and why one is facing an error while the other is not.
EDIT (I forgot to mention this initially): I am aware that I can duplicate the type check inside the parameter's type (as suggested by @AntonKastritskiy in his answer), but I prefer to avoid this repetition and seek a deeper understanding of the issue.
Perhaps there is a more efficient way to achieve this (especially in terms of preserving a reference to the current keyof T
during iteration, rather than breaking down the getter/setter names). While I attempted this as a personal challenge, I am now open to suggestions for improvement, if any 😊