I need to validate a ts inference like this one
interface Animal {
name: string;
color: string;
age: number;
}
type Validator<T> = (data: T) => boolean;
const validateString: Validator<string> = (data) => !!data?.length;
const validateNumber: Validator<number> = (data) => data > 0;
// this type should have ts inference
type ValidatorTuple<T> = [(data: T) => unknown, Validator<unknown>];
const validate = <T>(item: T, validator: ValidatorTuple<T>[]) => {
for (const [predicate, validation] of validator) {
const value = predicate(item);
const result = validation(value);
if (!result) return false;
}
return true;
};
//expected result
const dog: Animal = {
age: 12,
color: "white",
name: "josh",
};
const isValid = validate(dog, [
[(d) => d.age, validateNumber],
[(d) => d.color, validateString],
[(d) => d.name, validateNumber], // this should throw a TS error
]);
I've tried many ts inference but nothing works
CodePudding user response:
Turns out that your unknown
's were causing the compiler some trouble (see @geoffrey's comment). Changing them to any
allowed me to do this (it's OK since it won't "bleed" into the rest of the code):
const validate = <T, V extends [(data: T) => any, Validator<any>][]>(item: T, validator: [...{
[K in keyof V]: V[K] extends [(data: T) => infer U, Validator<infer X>]
? [U] extends [X]
? V[K]
: [TypeError, "Validator type does not match with value provider."]
: V[K];
}]) => {
Essentially, we're allowing the compiler to infer the type of V
first, then "looping" over V
and checking if the validator and "value provider" match. If it doesn't, we "replace" that element with a type error instead.
And as an unintended effect, the body of the function is unaffected. Mainly because the values are now any
- but that is OK (and technically correct).