TypeScript:指定参数必须包含与另一个参数匹配的键吗?

时间:2019-11-17 22:37:13

标签: typescript recursive-datastructures

我正在使用可处理链表的图书馆。目前,它被硬编码为接受一个列表节点类型,该列表节点类型包含指向下一个节点的“ next”字段,该字段属于同一类型,但是我希望它与用于链接的特定字段名称无关- -ie,是否碰巧正在处理通过“上一个”引用或“下一个”引用或“上一个”或“指针”或数字索引连接的列表节点(例如,如果您使用的是两个元素的数组来模拟cons单元格(例如)或其他方式,用户输入一个字符串,该字符串指示列表链接字段的名称是什么。

现在,我可以通过允许库使用符合具有any(即interface ListNode { [key: string]: any }的索引签名的开放接口的对象的类型不安全的方式来解决此问题,然后只相信用户提供一个确实具有他们指定的链接键的对象...但是我宁愿能够真正地检查将列表节点提供给库实际上确实具有带有库用户声称拥有的名称的字段。毕竟,这就是使用TypeScript的全部目的。

我可以做到这一点:

function doListStuff(head: { [ptr]: ??? }, ptr: string) { ... }

但是我不知道要为???输入什么?有没有办法做递归匿名类型?

我也尝试过这个:

interface ListNode<Ptr extends string> {
   [Ptr]: ListNode<Ptr> | null;
}

但这给了我以下错误列表:

  

[ts]接口中的计算属性名称必须引用一个   类型为文字类型或“唯一符号”类型的表达式。   [ts]找不到名称“ Ptr”。   [ts]计算出的属性名称无法从其包含的类型引用类型参数。

那么...有什么方法可以定义具有计算出的属性名称的接口?

或者通过其他任何方式来真正实现我想要的?

1 个答案:

答案 0 :(得分:0)

您可以使用mapped type来表示其键是某种类似于通用键的类型的对象。看起来像这样:

type ListNode<P extends string> = { [K in P]: ListNode<P> | null };

(请注意,上面的定义似乎不允许任何数据,但是您可以通过intersections等添加所需的任何其他结构)。

然后您的doListStuff()可能类似于:

function doListStuff<P extends string>(head: ListNode<P>, ptr: P) {
    const next: ListNode<P> | null = head[ptr];
    console.log(JSON.stringify(next));
    if (next) doListStuff(next, ptr);
}

您会看到它有效:

doListStuff({ foo: { foo: { foo: { foo: null } } } }, "foo");
//{"foo":{"foo":{"foo":null}}}
//{"foo":{"foo":null}}
//{"foo":null}
//null;

好的,希望能有所帮助;祝你好运!

Link to code