In lodash, the _.invert
function inverts an object's keys and values:
var object = { 'a': 'x', 'b': 'y', 'c': 'z' };
_.invert(object);
// => { 'x': 'a', 'y': 'b', 'z': 'c' }
The lodash typings currently declare this to always return a string
→string
mapping:
_.invert(object); // type is _.Dictionary<string>
But sometimes, especially if you're using a const assertion, a more precise type would be appropriate:
const o = {
a: 'x',
b: 'y',
} as const; // type is { readonly a: "x"; readonly b: "y"; }
_.invert(o); // type is _.Dictionary<string>
// but would ideally be { readonly x: "a", readonly y: "b" }
Is it possible to get the typings this precise? This declaration gets close:
declare function invert<
K extends string | number | symbol,
V extends string | number | symbol,
>(obj: Record<K, V>): {[k in V]: K};
invert(o); // type is { x: "a" | "b"; y: "a" | "b"; }
The keys are right, but the values are the union of the input keys, i.e. you lose the specificity of the mapping. Is it possible to get this perfect?
Edit
Since the introduction of the as
clause in mapped types you can write this type as:
You can use a mapped type with an as clause:
type InvertResult<T extends Record<PropertyKey, PropertyKey>> = {
[P in keyof T as T[P]]: P
}
Playground Link
Original answer
You can do it using a more complicated mapped type that preserves the correct value:
const o = {
a: 'x',
b: 'y',
} as const;
type AllValues<T extends Record<PropertyKey, PropertyKey>> = {
[P in keyof T]: { key: P, value: T[P] }
}[keyof T]
type InvertResult<T extends Record<PropertyKey, PropertyKey>> = {
[P in AllValues<T>['value']]: Extract<AllValues<T>, { value: P }>['key']
}
declare function invert<
T extends Record<PropertyKey, PropertyKey>
>(obj: T): InvertResult<T>;
let s = invert(o); // type is { x: "a"; y: "b"; }
Playground Link
AllValues
first creates a union that contains all key
, value
pairs (so for your example this will be { key: "a"; value: "x"; } | { key: "b"; value: "y"; }
). In the mapped type we then map over all value
types in the union and for each value
we extract the original key
using Extract
. This will work well as long as there are no duplicate values (if there are duplicate values we will get a union of the keys wehere the value appears)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With