In my quest to create a sophisticated form builder in React using TypeScript, I am intrigued by the potential for compile-time checks and eager to explore the implementation of a specific one. While articulating this question abstractly is challenging, a concise example would better demonstrate what I am trying to achieve.
Consider having a generic Input
type that represents an object with various properties. I wish to design a function called numericField
, which accepts an input object and a key K
such that Input[K]
is of type number
. If I provide a mistyped or non-numeric key, I expect the compiler to catch it and generate an error message.
For instance:
interface Person {
name: string
age: number
}
When calling the function, I anticipate the following outcomes:
decimalField<Person>({input: person, key: 'age'}) // should work as expected
decimalField<Person>({input: person, key: 'agge'}) // ideally triggers a compiler error
decimalField<Person>({input: person, key: 'name'}) // also should prompt a compiler error
I have achieved this using the following type definition:
export type PropertiesOfSubtype<T, P> = {
[K in keyof T]-?: Exclude<T[K], undefined | null> extends P ? K : never
}[keyof T]
By defining decimalField
as:
function decimalField<Input>(props: {input: Input, key: PropertiesOfType<Input, number>})
...it partially accomplishes the intended functionality. However, there's one critical issue.
I want TypeScript to infer that input[key]
returns a number, which it currently does not. I believe there might be a way to refactor the code so that TypeScript recognizes this fact during type checking and treats it accordingly.
My main inquiry is thus: Is there a more effective approach to achieving this?
P.S.: Here's a link to a playground illustrating the example, along with my projected next step – incorporating an optional/required argument based on whether a predefined label exists for the field or not.