It seems there are a few complexities at play here, prompting me to provide an answer as the existing questions do not directly address the situation.
When dealing with a type that includes an index signature, extracting only the "known" literal keys of the object proves challenging if those keys are subtypes of the index signature. For example,
keyof {[k: string]: any, foo: any}
results in just
string
, where
"foo"
is fully encompassed within that. To selectively extract only the known literal keys, a conditional type trick can be employed, as demonstrated in this related question:
type RemoveIndex<T> = {
[K in keyof T as string extends K ? never : number extends K ? never : K]: T[K]
};
type KnownKeys<T> = keyof RemoveIndex<T>;
On the other hand, isolating only the keys whose associated values meet specific criteria can be achieved through mapped-conditional-lookup, outlined in this related question:
type KeysMatching<T, V> = { [K in keyof T]: T[K] extends V ? K : never }[keyof T];
Combining these approaches yields:
type KnownKeysMatching<T, V> = KeysMatching<Pick<T, KnownKeys<T>>, V>
Verification of this implementation demonstrates its intended functionality:
function doStuff(arg: KnownKeysMatching<TheRecord, TypeA>): void {
}
doStuff('a'); // okay
doStuff('b'); // error!
doStuff('c'); // okay
doStuff('d'); // error!
Observe how the variable arg
cannot be assigned 'b'
as per specifications, and similarly cannot be 'd'
or any other "unknown" string, despite TheRecord
having a string index signature. If adjustments are needed for handling 'd'
differently, it might require additional modifications beyond the initial scope of the inquiry.
Playground link to code