If you want to identify keys that correspond to array types, you can achieve this by utilizing the following approach (found it here):
type ArrayKeys<T> = {
[Key in keyof T]: T[Key] extends any[] ? Key : never;
}[keyof T];
(The explanation for this is provided below.) For example,
ArrayKeys<IInitialFormValues>
would simply be
FormKeys.b
, as it is the only property with an array type. If there are multiple properties with arrays,
ArrayKeys<IInitialFormValues>
would represent a union of those keys.
Subsequently, the type for your input
parameter should be
ArrayKeys<IInitialFormValues>
:
function onChange(input: ArrayKeys<IInitialFormValues>) {
initialFormValues[input] = [];
}
This method ensures that the following function call works as intended:
onChange(FormKeys.b); // Works as desired
While this one fails gracefully:
onChange(FormKeys.a); // Fails as desired
You can see a live demonstration on the TypeScript Playground type ArrayKeys<T> = {
[Key in keyof T]: T[Key] extends any[] ? Key : never;
}[keyof T];
There are two steps involved in this process:
The section enclosed in {/*...*/}
maps each property of T
to a new mapped type where the property's type serves as the key or returns never
. Using the example:
type Example = {
a: string[];
b: number[];
c: string;
};
The first part of ArrayKeys
creates the following anonymous type:
{
a: "a";
b: "b";
c: never;
}
Lastly, the [keyof T]
portion at the end generates a union of the property types. In theory, this would result in
"a" | "b" | never
, but since never
gets removed from unions, we end up with just "a" | "b"
— representing the keys of Example
for properties with array types.
You could further refine this and construct a type that specifically includes portions of Example
matching these criteria using
Pick<Example, ArrayKeys<Example>>
. However, such refinement is unnecessary for the current context.