繁体   English   中英

使用打字稿将对象文字映射到另一个对象文字

[英]Map an Object Literal to Another Object Literal Using Typescript

是否可以使用Lodash样式的“ mapValues”和“ mapKeys”函数,该函数返回映射的对象文字-而不是(过大的)通用记录类型?

我的意思是:

_.mapValues({a:1, b:2}, (v) => v === 1 ? 'aaa' : 'bbb') 

此代码(Lodash库)返回Record<'a' | 'b', 'aaa' | 'bbb'> Record<'a' | 'b', 'aaa' | 'bbb'> Record<'a' | 'b', 'aaa' | 'bbb'> ,而不是文字类型{a: 'aaa', b: 'bbb'}

与Ramda / Fp-ts函数相同-某些类型信息丢失。

我认为TypeScript编译器没有为高阶类型分析提供足够的支持来为您执行此操作。 我看到的问题:

  • 编译器没有好的方法来推断函数const mapper = (v: 1 | 2) => v === 1 ? "aaa" : "bbb" const mapper = (v: 1 | 2) => v === 1 ? "aaa" : "bbb"具有条件通用类型,例如<V extends 1 | 2>(v: V) => V extends 1 ? "aaa" : "bbb" <V extends 1 | 2>(v: V) => V extends 1 ? "aaa" : "bbb" <V extends 1 | 2>(v: V) => V extends 1 ? "aaa" : "bbb"{(v: 1): "aaa", (v: 2): "bbb"}类的重载函数类型。 如果希望编译器像对待函数一样,则必须手动声明或注释类型。

  • 即使可以推断出该值,也无法编写类似Apply<typeof f, typeof x>的类型函数Apply<typeof f, typeof x>其中f是一个参数的重载或通用函数,而x是其可接受的参数,因此Apply<typeof f, typeof x>f(x)的类型。 更简短: TypeScript中没有typeof f(x) 因此,尽管您可以调用 mapper(1)并且编译器知道结果为"aaa"类型,但是您无法在类型系统中表示该知识。 这样可以防止您在类型系统中执行重载函数解析泛型函数解析之类的操作。


我能想到的最简单的_.mapValues将为您提供_.mapValues Record的宽类型,如果需要,您必须断言一个较窄的类型:

declare namespace _ {
  export function mapValues<T, U>(
    obj: T,
    fn: (x: T[keyof T]) => U
  ): Record<keyof T, U>;
}
const obj = { a: 1, b: 2 } as const;
type ExpectedRet = { a: "aaa"; b: "bbb" };
_.mapValues(obj, v => (v === 1 ? "aaa" : "bbb")); // Record<"a"|"b", "aaa"|"bbb">
const ret = _.mapValues(obj, v => (v === 1 ? "aaa" : "bbb")) as ExpectedRet;

否则,您将不得不跳过许多熊熊的循环(手动指定类型,将函数手动声明为重载),最终会得到比类型断言安全得多但复杂得多的东西:

type UnionToIntersection<U> = (U extends any
  ? (k: U) => void
  : never) extends ((k: infer I) => void)
  ? I
  : never;

declare namespace _ {
  export function mapValues<T, U extends Record<keyof T, unknown>>(
    obj: T,
    fn: UnionToIntersection<{ [K in keyof T]: (x: T[K]) => U[K] }[keyof T]>
  ): U;
}

function mapper(v: 1): "aaa";
function mapper(v: 2): "bbb";
function mapper(v: 1 | 2): "aaa" | "bbb" {
  return v === 1 ? "aaa" : "bbb";
}

const obj = { a: 1, b: 2 } as const;
type ExpectedRet = { a: "aaa"; b: "bbb" };
const ret = _.mapValues<typeof obj, ExpectedRet>(obj, mapper);

不知道这是否值得解释...您必须在对_.mapValues的调用中手动指定输入和期望的输出类型,因为编译器无法推断出输出类型(如上所述)。 您必须手动指定mapper是重载函数。 _.mapValues的键入很复杂,并使用UnionToIntersection将所需的重载函数描述为将输入值转换为输出值的函数类型的交集。

因此,我会远离这个,只使用类型断言。


希望能有所帮助; 对不起,我没有更令人满意的答案。 祝好运!

链接到代码

我知道我在某处看到过它。 但是我想你想要这个。

const mapped = Object.assign({}, ...Object.keys(ori).map((key) => {return {[key]: /*...*/}}))

例:

JS:

const x = {a: 1, b: 2};
const y = Object.assign({}, ...Object.keys(x).map((key) => ({[key]: x[key] + 1})));

// y = {a: 2, b: 3}

TS:

const x: Record<string, number> = {a: 1, b: 2};
const y = Object.assign({}, ...Object.keys(x).map((key) => ({[key]: x[key] + 1})));

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM