I wrote a fairly straightforward mapped-types based code, which doesn\'t want to type check for some reason.
First, define input and output:
interfac
The compiler is protecting you against something unlikely to happen, and you need to decide how to work around it (spoiler alert: use a type assertion)
Imagine if I do this:
const field = Math.random() < 0.5 ? "name" : "price";
const value = Math.random() < 0.5 ? "Widget" : 9.95;
update(field, value); // no error
In this case, field is of the type FieldKey and value is of the type FieldInputs[FieldKey], and there is a 50% chance that they don't match up. Despite this, the compiler does not warn you: it infers that F is FieldKey (which is a perfectly valid thing for it to do), and the call to update() is allowed.
Inside the implementation of update(), there is the warning that FieldParsers[F] might not be a FieldParser. If F is FieldKey as above, this mismatch becomes apparent. FieldParsers[F] will be FieldParser<'name'> | FieldParser<'price'>, but FieldParser is FieldParser<'name' | 'price'>. The former is either something that parses a string or something that parses a number. The latter is something that parses either a string or a number. These are not the same (due to contravariance of function parameters enabled with --strictFunctionTypes). The difference between these types is exposed when the code above ends up calling update("name", 9.95) and you try to parse a number with a string parser. You want a FieldParser, but all you have is a FieldParsers[F].
Now backing up, is someone likely to play games like this where F is a union of values? If so, then you might want to change your definition of update() to explicitly prohibit F from being anything but a single string literal. Something like...
type NotAUnion =
U extends any ? [T] extends [U] ? T : never : never;
declare function update(
field: F & NotAUnion,
value: FieldInputs[F]
);
But that is probably overkill, and it still doesn't resolve the warning inside the implementation of update(). The compiler is simply not smart enough to understand that the value of F is a single string literal value and that what you are doing is safe.
To make that error go away, you will probably want to do a type assertion. Either you know that nobody is likely to intentionally shoot themselves in the foot by widening F to FieldKey, or you have prevented the caller from doing this by using something like NotAUnion. In either case, you can tell the compiler that you know that fieldParsers[field] will be a valid FieldParser:
function update(field, value: FieldInputs[F]) {
const parser = fieldParsers[field] as FieldParser; // okay
parser.apply(value);
}
So that works. Hope that helps. Good luck!