Reputation: 137
I need to test each value of multiple HTMLInputElement
s 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
Upvotes: 4
Views: 2177
Reputation: 327994
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!
Upvotes: 5