Consider the following overloaded Typescript function (attempt):
function eraseField(o : Record<"field", string>, nullify : false) : void
function eraseField(o : Record<"field", string | null>, nullify : true) : void
function eraseField(o : any, nullify : any) : void
{
if (nullify)
{
o.field = null;
}
else
{
o.field = "";
}
}
Now, consider the following object:
let o : Record<"field", string> = {"field" : "toBeErased"};
Unfortunately, we can do the following without any errors:
eraseField(o, true); // 'o.field' is no longer a string, which is against the type declaration.
How can I tweak the function declarations/definitions so that the above line of code gives a Typescript error (in strict mode)? In particular, I want to prevent the widening of Record<"field", string> to Record<"field", string | null> when it is passed to "eraseField", thus effectively making the Record type contravariant for its values in that context.
You can use a constrained generic type parameter in your relevant overload signature, which will allow you to use the compiler's inference to conditionally determine whether the parameter's field
value allows for a nullable value. In the case that it does, you can accept it — and in the case that it doesn't you can deny its use by setting the parameter's type to never
:
function eraseField(o: Record<"field", string>, nullify: false): void;
function eraseField<T extends Record<"field", string | null>>(
o: T extends Record<"field", string> ? never : T,
nullify: true,
): void;
function eraseField(o: Record<"field", string | null>, nullify: boolean) {
if (nullify) o.field = null;
else o.field = "";
}