Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
609 views
in Technique[技术] by (71.8m points)

lodash - Is it possible to precisely type _.invert in TypeScript?

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 stringstring 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?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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"; }

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)


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...