打字稿:当返回具有相同键但不同值的对象时,类型别名会导致奇怪的类型
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类型比较冗长,在return
ing的时候不得不重复。但至少它产生了正确的类型,当我将鼠标悬停在 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_1
和 values_2
上以查看差异。
为什么提取return类型时类型显示不正确?我是不是做错了什么?
打字稿中类型的打印定义不明确,并且根据反馈经常更改。在大多数情况下,typescript 会尝试保留类型别名(因为它们可能有意义)。如果您使用将得到解析的内联类型,因为没有其他选择。
在撰写本文时,有一个技巧可以强制扩展类型别名(尽管不能保证这在多大程度上总是有效)。您可以使用空对象类型的交集。这将导致编译器在不改变其结构的情况下扩展类型:
type ThingValuesMap<T> = {} & {
[key in keyof T]: T[key] extends Thing<infer ValueType> ? ValueType : never
}
我实现了一个函数(我们称它为 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类型比较冗长,在return
ing的时候不得不重复。但至少它产生了正确的类型,当我将鼠标悬停在 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_1
和 values_2
上以查看差异。
为什么提取return类型时类型显示不正确?我是不是做错了什么?
打字稿中类型的打印定义不明确,并且根据反馈经常更改。在大多数情况下,typescript 会尝试保留类型别名(因为它们可能有意义)。如果您使用将得到解析的内联类型,因为没有其他选择。
在撰写本文时,有一个技巧可以强制扩展类型别名(尽管不能保证这在多大程度上总是有效)。您可以使用空对象类型的交集。这将导致编译器在不改变其结构的情况下扩展类型:
type ThingValuesMap<T> = {} & {
[key in keyof T]: T[key] extends Thing<infer ValueType> ? ValueType : never
}