是否可以有 Lodash 风格的“mapValues”和“mapKeys”函数,返回映射的对象文字 - 而不是(太)通用的记录类型?
我的意思是:
_.mapValues({a:1, b:2}, (v) => v === 1 ? 'aaa' : 'bbb')
此代码(Lodash 库)返回 Record<'a' | 'b', 'aaa' | 'bbb'>
,而不是文字类型 {a: 'aaa', b: 'bbb'}
与 Ramda/Fp-ts 函数相同 - 一些类型信息丢失。
最佳答案
我认为 TypeScript 编译器没有足够的支持高阶类型分析来为您执行此操作。我看到的问题:
编译器没有好的方法来推断函数
const mapper = (v: 1 | 2) => v === 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>
这样的类型函数。哪里f
是一个参数的重载函数或泛型函数,且x
是一个可接受的参数,因此Apply<typeof f, typeof x>
类型为f(x)
。较短:there's notypeof f(x)
in TypeScript 。因此,虽然您可以调用mapper(1)
并且编译器知道结果的类型为"aaa"
,您无法在类型系统中表示该知识。这会阻止您执行类似 overloaded function resolution 的操作或generic function resolution在类型系统中。
我能想到的最简单的输入 _.mapValues
会给你宽Record
-like 类型,你必须 assert如果你想要的话,可以使用更窄的类型:
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
将所需的重载函数描述为将输入值转换为输出值的函数类型的交集。
所以,我会远离这个,只使用类型断言。
<小时/>希望有帮助;抱歉我没有更满意的答案。祝你好运!
关于javascript - 使用 Typescript 将一个对象文字映射到另一个对象文字,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57645058/