I have an object type where keys map to different types:
type Value = {
str: string;
num: number;
};
And I’m trying to implement a universal format
function:
const format = <K extends keyof Value>(key: K, value: Value[K]): string => {
if (key === 'str') {
// @ts-expect-error -- Property 'split' does not exist on type 'string | number'
return value.split('').join('');
}
if (key === 'num') {
// @ts-expect-error -- The left-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type
return `${value - 1}`;
}
return '';
};
But why is TypeScript unable to narrow down value
’s type inside an if
condition?
I have an alternative working solution, but the function signature is quite inconvenient:
type Value =
| ['str', string]
| ['num', number];
const format = (...[key, value]: Value): string => {
if (key === 'str') {
return value.split('').join('');
}
if (key === 'num') {
return `${value - 1}`;
}
return '';
};
Full example (playground link):
type Value = {
str: string;
num: number;
};
type KVTuple<T> = {
[P in keyof T]: [key: P, value: T[P]];
}[keyof T];
type X = KVTuple<Value>;
const format = (...[key, value]: KVTuple<Value>): string => {
if (key === "str") {
return value.split("").join("");
}
if (key === "num") {
return `${value - 1}`;
}
return "";
};
format("str", "text"); // Works
format("num", 1); // Works
format("str", 1); // Error as desired
format("num", "text"); // Error as desired
format<"str" | "num">("str", 42); // Error as desired