打字稿:当返回具有相同键但不同值的对象时,类型别名会导致奇怪的类型

Typescript: Type alias results in strange type when returning object with same keys but different values

我实现了一个函数(我们称它为 valuesOfThings),该函数采用具有任意键名和特定对象类型 Thing(实际上是 Map)的值的对象。然后该函数应该 return 一个具有相同键的对象,但是每个键的值应该是 Thing.

的某个键的值

例如我想转换为:

const things = {
    someThing: { value: 10 },
    otherThing: { value: 'foo'},
}

对此:

const values = {
    someThing: 10,
    otherThing: 'foo',
}

并且我希望 returned 对象被正确键入

该函数的 JS 实现如下所示:

function valuesOfThings(things) {
    const values = Object.fromEntries(
        Object.entries(things)
            .map(([key, thing]) => [key, thing.value])
    //                                   ^^^^^^^^^^^----- the important part
    )
    return values
}

基于 this question 我设法制作了以下 return 类型:

{[key in keyof T]: T[key] extends Thing<infer ValueType> ? ValueType : never}

让我们看看实际效果:

type Thing<T> = {
    value: T
}
type ThingMap = {
    [key: string]: Thing<unknown>
}

function valuesOfThings_1<T extends ThingMap>(things: T): {[key in keyof T]: T[key] extends Thing<infer ValueType> ? ValueType : never} {
    const values = Object.fromEntries(
        Object.entries(things)
            .map(([key, thing]) => [key, thing.value])
    )
    return values as {[key in keyof T]: T[key] extends Thing<infer ValueType> ? ValueType : never}
}

可以看到,return类型比较冗长,在returning的时候不得不重复。但至少它产生了正确的类型,当我将鼠标悬停在 returned 变量上时,我看到了我期望的类型:

Bu 当我尝试像这样提取这种类型时:

type ThingValuesMap<T> = {
    [key in keyof T]: T[key] extends Thing<infer ValueType> ? ValueType : never
}

function valuesOfThings<T extends ThingMap>(things: T): ThingValuesMap<T>  {
    return Object.fromEntries(
        Object.entries(things)
            .map(([key, thing]) => [key, thing.value])
    ) as ThingValuesMap<T>
}

结果类型未正确解析:

但是,智能感知知道正确的类型:

如您所见,otherThing 的类型正确显示为 string

我创建了一个 TS 游乐场 here。将鼠标悬停在 values_1values_2 上以查看差异。

为什么提取return类型时类型显示不正确?我是不是做错了什么?

打字稿中类型的打印定义不明确,并且根据反馈经常更改。在大多数情况下,typescript 会尝试保留类型别名(因为它们可能有意义)。如果您使用将得到解析的内联类型,因为没有其他选择。

在撰写本文时,有一个技巧可以强制扩展类型别名(尽管不能保证这在多大程度上总是有效)。您可以使用空对象类型的交集。这将导致编译器在不改变其结构的情况下扩展类型:


type ThingValuesMap<T> = {} & {
    [key in keyof T]: T[key] extends Thing<infer ValueType> ? ValueType : never
}

Playground Link