I need to test each value of multiple HTMLInputElements using a/some test function(s), and auto-generate a nested list of checkboxes for each, with the conditions which are expressed as Conditions below:
type TestFunction = (value: string) => boolean
type TestFunctionObject = { [description: string]: Conditions }
type Conditions = TestFunction | TestFunctionObject
So, for example, if I have:
const conds1: Conditions = {
  "Consists of alphanumerics": /^[a-zA-Z0-9]$/.test
}
I get a checkbox labeled as "Consists of alphanumerics". And if:
const conds2: Conditions = /^[a-zA-Z0-9]$/.test
I don't want a checkbox but just validate with that.
Auto-generating is done without any problem. Then, I wrote a type which represents validity of each TestFunction:
type Validity<C extends Conditions> = C extends TestFunction
  ? boolean
  : { [K in keyof C]: Validity<C[K]> }
Now I got an error from TS on C[K]; playground here. It says Type 'Conditions[K]' is not assignable to type 'TestFunctionObject'. Type conditioning doesn't seem to narrow Conditions to just TestFunctionObject.
How can I get it to work?
Addition for jcalz's answer: Playground with examples
I don't think the compiler does a lot of narrowing within the else clause of conditional types (after :)  the way it does with values via control flow analysis.  So while it's obvious to you that in the conditional type C extends TestFunction, the C in the else clause should extend TestFunctionObject, the compiler doesn't realize it.  
But the compiler does do narrowing within the then clause (between ? and :), so the easiest fix for this is to add another conditional type:
type Validity<C extends Conditions> = C extends TestFunction ? boolean
  : C extends TestFunctionObject ? { [K in keyof C]: Validity<C[K]> } : never
Note that the last conditional type has never as the else clause.  That's a common idiom with conditional types; sometimes you know the else clause cannot be reached; and in the absence of an invalid type, the never type is a good alternative.
Or, since you weren't doing much with the then clause to begin with, flip the clauses of your original check:
type Validity<C extends Conditions> = C extends TestFunctionObject ?
  { [K in keyof C]: Validity<C[K]> } : boolean
Either should work. Hope that helps; good luck!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With