【问题标题】:Typescript: Type alias results in strange type when returning object with same keys but different values [duplicate]打字稿:返回具有相同键但值不同的对象时,类型别名会导致奇怪的类型[重复]
【发布时间】:2022-01-07 09:40:57
【问题描述】:

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

例如我想转换这个:

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

到这里:

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

并且我希望正确输入返回的对象

函数的JS实现如下:

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

基于this question,我设法制作了以下返回类型:

{[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}
}

如您所见,返回类型比较长,returning 时必须重复。但至少它会产生正确的类型,当我悬停返回的变量时,我会看到我期望的类型:

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

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 以查看区别。

为什么提取返回类型时类型显示不正确?我在这里做错了吗?

【问题讨论】:

    标签: typescript static-typing


    【解决方案1】:

    typescript 中类型的打印不是很好定义的,并且会根据反馈频繁更改。在大多数情况下,打字稿会尝试保留类型别名(因为它们可能有意义)。如果您使用将得到解决的内联类型,因为没有其他替代方案。

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

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

    Playground Link

    【讨论】:

    • 正要写一个类似的答案,但现在似乎不值得。我的方法是一样的,但是我使用这个助手扩展了类型:type Expand&lt;T&gt; = T extends infer O ? { [K in keyof O]: O[K] } : never; for type TVM_Expanded&lt;T&gt; = Expand&lt;ThingValuesMap&lt;T&gt;&gt;
    • @spender 这也有效,看起来还不错。我更喜欢我的版本,因为我认为它可能更具侵入性。条件类型可能会使可分配性更难锻炼(尤其是在涉及类型参数的情况下)。它可能像条件类型通常那样在计算上更加密集。但要么工作?‍♀️
    • 我不知道与{} 相交。一个用于工具带?
    猜你喜欢
    • 2017-05-20
    • 1970-01-01
    • 2019-09-29
    • 2019-12-18
    • 2015-10-26
    • 2019-03-13
    • 2020-11-16
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多