我正在尝试调整我已经在TypeScript中编写的现有流程,以便更强类型化。
基本上,我有一个如下所示的树结构:
interface Node {
[name: string]: Leaf | Node | Node[] | undefined;
}
这是一个简单的树,其中每个节点包含由属性名称组织的叶节点,另一个节点,节点列表或未定义。
我有一种机制可以在这棵树上运行规则。规则如下:
interface Rule<T extends Node, K = keyof T> {
name: K;
optional?: boolean;
data?: {}; // matches against the structure of a child Leaf
list?: boolean; // whether this child should be a list
process?(processor: RuleProcessor): Node; // for processing sub-nodes
// ...other flags and stuff specific to the rule
}
我想通过指定适用于不同类型的孩子的不同类型的规则来使其更通用:
optional
仅适用于可能未定义的儿童data
仅适用于落叶的儿童list
仅适用于列表中的子项(在这种情况下,它始终为真)process()
仅适用于作为节点或节点列表的子项这就是我想象的:
type Child = Leaf | Node | Node[];
interface Node {
[name: string]: Child | undefined;
}
interface Rule<T extends Node, K extends keyof T = keyof T, _V extends T[K] = T[K]> {
name: K;
}
interface OptionalRule<T extends Node, K extends keyof T = keyof T> extends Rule<T, K, Child | undefined> {
optional: true;
}
interface RequiredRule<T extends Node, K extends keyof T = keyof T, V extends Child = Child> extends Rule<T, K, V> {}
interface LeafRule<T extends Node, K extends keyof T = keyof T> extends RequiredRule<T, K, Leaf> {
data: {};
}
interface ListRule<T extends Node, K extends keyof T = keyof T> extends RequiredRule<T, K, Node[]> {
list: true;
}
interface ProcessRule<T extends Node, K extends keyof T = keyof T> extends RequiredRule<T, K, Node | Node[]> {
process(processor: RuleProcessor): Node;
}
type AnyRule<T extends Node> = OptionalRule<T> | LeafRule<T> | ListRule<T> | ProcessRule<T>;
function process<T extends Node>(node: T, rules: AnyRule<T>[]) {
// logic
}
我的想法是,如果我为特定属性指定规则,则所选规则类型将根据属性的类型进行检查。
例如,假设我有一个节点:
const node: Node = { a: new Leaf(), b: [] };
我处理它:
process(node, [{ name: 'a', data: {} }, { b: list: true }]);
我希望指定data
会以某种方式检查以确保node['a']
是Leaf
,如果不是,则会给出错误。同样,第二条规则会检查以确保node['b']
是Node[]
。
但是,当我在data: {}
上将list: true
更改为a
时,即使node['a']
不是数组,也不会出错。我知道我从一开始就做错了什么,但我想知道TypeScript中是否有类似的东西。我知道有很多这些新功能,如keyof T
和T[K]
,我们能够描述一些非常复杂的类型,所以我希望能够实现我想要做的事情。
我的目标是将编译时检查到位,以便开发人员不会犯错误。显然,这些都不需要在运行时应用,因为类型都被剥离了。
编辑:基本上,如果我有办法在V = T[K]
V
上设置work <- function(n) {
# Do some intensive work (e.g explore a graph starting at n).
# After this, we don't need to execute work() on nodes in excluding.
# (e.g exclude could be the nodes explored/reached from n)
# n is just an example. exclude can be a potentially large set.
Sys.sleep(2)
exclude <- c(n, sample(nodes, rbinom(1, length(nodes), 0.5)))
return(exclude)
}
nodes <- 1:1e3
#Order of execution doesn't matter
nodes <- sample(nodes)
#parallelize this loop
while(length(nodes) > 0) {
n <- nodes[1]
exclude <- work(n)
nodes <- setdiff(nodes, exclude)
}
必须是特定类型的约束,这个问题就会解决。
答案 0 :(得分:-1)
因此,事实证明这是目前可以用映射类型解决的!而且无需定义Node
类型:
type Child = Leaf | Node | Node[];
interface Rule<T extends { [P in K]: V }, K extends keyof T, V> {
name: K;
}
interface OptionalRule<T extends { [P in K]?: V }, K extends keyof T, V> extends Rule<T, K, Child | undefined> {
optional: true;
}
interface RequiredRule<T extends { [P in K]: V }, K extends keyof T, V extends Child> extends Rule<T, K, V> {}
interface LeafRule<T extends { [P in K]: Leaf }, K extends keyof T> extends RequiredRule<T, K, Leaf> {
data: {};
}
interface ListRule<T extends { [P in K]: V[] }, K extends keyof T, V> extends RequiredRule<T, K, V[]> {
list: true;
}
interface ProcessRule<T extends { [P in K]: V | V[] }, K extends keyof T, V> extends RequiredRule<T, K, V | V[]> {
process(processor: RuleProcessor): V;
}
type AnyRule<T extends { [P in K]: V }, K extends keyof T, V> = OptionalRule<T, K, V> | LeafRule<T, K> | ListRule<T, K, V> | ProcessRule<T, K, V>;
function process<T extends { [P in K]: V }, K extends keyof T, V>(node: T, rules: AnyRule<T, K, V>[]) {
// logic
}
有点冗长,但它运作正常!可以推断出函数类型参数。