是否可以精确键入_,在TypeScript中反转?

g52tjvyc  于 2023-05-01  发布在  TypeScript
关注(0)|答案(3)|浏览(139)

在lodash中,_.invert函数反转对象的键和值:

var object = { 'a': 'x', 'b': 'y', 'c': 'z' };

_.invert(object);
// => { 'x': 'a', 'y': 'b', 'z': 'c' }

lodash类型当前声明它总是返回stringstringMap:

_.invert(object);  // type is _.Dictionary<string>

但有时候,特别是当你使用constAssert时,更精确的类型会更合适:

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

打字能打得这么精确吗?这个声明接近:

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

键是正确的,但值是输入键的并集,i。即,你失去了Map的特异性。有可能做到完美吗?

6tqwzwtp

6tqwzwtp1#

编辑

由于在Map类型中引入了as子句,因此可以将此类型写成:
您可以使用带有as子句的Map类型:

type InvertResult<T extends Record<PropertyKey, PropertyKey>> = {
  [P in keyof T as T[P]]: P
}

Playground链接

原始答案

你可以使用一个更复杂的Map类型来实现,它可以保留正确的值:

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链接
AllValues首先创建一个包含所有keyvalue对的并集(因此对于示例,这将是{ key: "a"; value: "x"; } | { key: "b"; value: "y"; })。在Map的类型中,我们Map联合中的所有value类型,对于每个value,我们使用Extract提取原始key。只要没有重复的值,这将工作得很好(如果有重复的值,我们将得到一个键的并集,在这里值出现)

a1o7rhls

a1o7rhls2#

Titian Cernicova-Dragomir的解决方案非常酷。今天,我发现了另一种用条件类型交换对象键和值的方法:

type KeyFromValue<V, T extends Record<PropertyKey, PropertyKey>> = {
  [K in keyof T]: V extends T[K] ? K : never
}[keyof T];

type Invert<T extends Record<PropertyKey, PropertyKey>> = {
  [V in T[keyof T]]: KeyFromValue<V, T>
};

使用const o测试:

const o = {
  a: "x",
  b: "y"
} as const;

// type Invert_o = {x: "a"; y: "b";}
type Invert_o = Invert<typeof o>;

// works
const t: Invert<typeof o> = { x: "a", y: "b" };
// Error: Type '"a1"' is not assignable to type '"a"'.
const t1: Invert<typeof o> = { x: "a1", y: "b" };

声明invert函数的方式与上面的答案相同,返回类型为Invert<T>
Playground

fjnneemd

fjnneemd3#

这在TypeScript 4中变得非常简单。1对Map类型中的键重Map的支持:

const o = {
    a: 'x',
    b: 'y',
} as const;

declare function invert<
    T extends Record<PropertyKey, PropertyKey>
>(obj: T): {
    [K in keyof T as T[K]]: K
};

let s = invert(o);  // type is { readonly x: "a"; readonly y: "b"; }

Playground

相关问题