【发布时间】:2021-03-06 05:17:18
【问题描述】:
我怎样才能使用{ 'k': number, [s: string]: any } 类型并抽象'k' 和number?我想有一个类型别名 T 这样 T<'k', number> 给出上述类型。
考虑以下示例:
function f(x: { 'k': number, [s: string]: any }) {} // ok
type T_no_params = { 'k': number, [s: string]: any }; // ok
type T_key_only<k extends string> = { [a in k]: number }; // ok
type T_value_only<V> = { 'k': V, [s: string]: any}; // ok
type T_key_and_index<k extends string, V> = { [a in k]: V, [s: string]: any };// ?
- 直接使用
{ 'k': number, [s: string]: any}作为函数f的参数类型有效。 - 在
type-alias 中使用[s: string]: any索引部分有效 - 在
type-alias 中使用k extends string也可以 - 将
k extends string与[s: string]: any组合在同一个type-alias 中时,我收到一个解析错误(甚至不是语义错误,它甚至不是有效的语法)。
这似乎可行:
type HasKeyValue<K extends string, V> = { [s: string]: any } & { [S in K]: V }
但是在这里,我不太明白为什么它不抱怨额外的属性(& 右侧的类型不应该允许具有额外属性的对象)。
编辑:
在答案中多次提到& 是交集运算符,它的行为应该类似于集合论交集。然而,在处理额外属性时,情况并非如此,如下例所示:
function f(x: {a: number}){};
function g(y: {b: number}){};
function h(z: {a: number} & {b: number}){};
f({a: 42, b: 58}); // does not compile. {a: 42, b: 58} is not of type {a: number}
g({a: 42, b: 58}); // does not compile. {a: 42, b: 58} is not of type {b: number}
h({a: 42, b: 58}); // compiles!
在这个例子中,似乎{a: 42, b: 58} 既不是{a: number} 类型,也不是{b: number} 类型,但它以某种方式结束于{a: number} & {b: number} 的交叉点。这不是集合论交集的工作原理。
这正是我自己的&-proposal 看起来如此可疑的原因。如果有人能详细说明如何将映射类型与{ [s: string]: any }“相交”可以使类型“更大”而不是更小,我将不胜感激。
我看过问题
但它们似乎没有直接关系,尽管名称相似。
【问题讨论】:
-
&右侧的类型本身不允许额外的属性。通过使用(诚然名字不好的)交集类型运算符&,您声明了一个在所谓的交集中具有所有类型的所有属性的类型,即[S in K]: V以及所有[s: string]: any。跨度> -
类型系统 navel gazing 没有什么问题,大声笑,但是您是否有一个要解决的实际问题导致您提出这个问题?也许发布一些除了签名之外的假设代码将有助于阐明您希望如何利用类型系统来实现更具体的东西。您是否希望在调用方、在使用签名的函数内、方便的类型声明等方面提供更好的智能感知?解决问题的惯用方法可能不需要这种特殊方法。
-
@rob3c 不确定你所说的“肚脐凝视”是什么意思——我从来没有为打字稿编译器做过贡献。最初的问题非常实用:我不得不将看起来很奇怪的
[s: string]: any添加到多种类型中,以便在我不想要的地方停用额外属性检查,我在问自己是否可以提取@987654353 @part 转换成单独的类型定义,以减少混乱。 -
不确定为什么要提到对 TS 编译器的贡献?原始问题只有声明/签名,没有具体的使用示例,这可能仅意味着理论而非实际兴趣。我并不是说没有实际应用。正如我所说,理论问题是可以的。我建议描述假设的用法,以防您还试图解决实际问题。一个原因是人们经常在他们的问题中采用解决方案的形式,而不是更直接地陈述问题。赏金还询问了惯用用法。
-
对于那些未在编辑中编译的函数示例,您将对象文字作为参数传递。对象字面量很特殊,只能指定已知属性。我相信这个想法是丢弃额外的属性数据可能是一个错误,因为它不会在那个时候出现在其他任何地方。但是,您可以将带有额外 props 的变量传递给这些函数,只要它们的 props 子集与签名匹配。即
const ab = { a: 42, b: 58 }可以作为f(ab); g(ab); h(ab);传递而不会出现编译错误。
标签: typescript types mapped-types