你能创建一个 Typescript 类型,它恰好具有一组属性中的一个并且可以被该组索引吗
Can you create a Typescript type that has exactly one of a set of properties AND is indexable by that set
我有以下类型:
type OrBranch = {
or: Branch[]
}
type AndBranch = {
and: Branch[]
}
我想要一个 Branch
类型,它可以是 OrBranch
或 AndBranch
。所以我首先尝试:
type Branch = AndBrand | OrBranch
工作很棒,除非我想做类似的事情:
let branch: Branch = ...
let andOr = 'and'; // or 'or'
let nodes = branch[andOr]
然后我发现 branch
不可索引。好的,所以我尝试使用可索引类型:
type AndOr = 'and' | 'or';
type Branch = Record<AndOr, Branch[]>;
但这需要 and
和 or
都存在,所以在这种情况下我不能将 AndBranch 转换为 Branch。
同样
type Branch = Record<AndOr, Branch[]> | AndBranch | OrBranch
不起作用,原因相同。
虽然我可以使用类型保护来确定类型,但我有对这些对象进行操作的长函数,在这些对象中,除了 属性 之外,它们可以被同等对待。我希望通过使用 andOr
变量来消除一堆重复代码,类型保护并不能真正阻止。例如:
let retval = {} as Branch;
if (isAnd(branch)) { // branch is a Branch parameter passed in
(retval as AndBranch).and = [] as Branch[];
set = (retval as AndBranch).and;
} else {
(retval as OrBranch).or = [] as Branch[];
set = (retval as OrBranch).or;
}
set = _.reduce(set, (all, item: Branch)=> {
if (isAnd(branch) && isAnd(item))
return _.union(all, item.and);
else if (isOr(branch) && isOr(item))
return _.union(all, item.or);
else
return all;
}, [] as Branch[]);
对比
andOr = isAnd(branch) ? 'and' : 'or';
let retval = {} as Branch;
retval[andOr] = _.reduce(set, (all, item: Branch) => {
if (item[andOr])
return _.union(all, item[andOr]);
else
return all;
}, [] as Branch[]);
我知道有一种方法可以精确地要求 and
和 or
之一(就像对 的回答)。但是那个类型是不可索引的。
是否可以同时获得两种效果?
使类型可索引并不能解决基本问题,即您试图在可能是 and
分支的对象上使用 属性 and
(因此有 属性) 或者可能是 or
分支(因此不是)。相反,询问分支它有什么并使用它,因为这允许 TypeScript 缩小类型:
if ("and" in branch) {
// ...use `branch.and`...
} else {
// ...use `branch.or`...
}
如果你的代码认为它知道分支是什么,你可以将它与你的 andOr
结合起来,也许通过使用类型断言函数:
function assertIsAndBranch(branch: Branch): asserts branch is AndBranch {
if (!("and" in branch)) {
throw new Error(`branch is not an AndBranch`);
}
}
// (And `assertIsOrBranch`)
然后:
if (andOr === "and") {
assertIsAndBranch(branch);
// ...here, TypeScript knows `branch` is an `AndBranch`...
}
重新编辑:如果您有重要代码需要在不知情的情况下处理 AndBranch
的 and
或 OrBranch
的 or
中包含的项目或者关心它们是 AndBranch
还是 OrBranch
实例,我将它们重新设计为 discriminated union,其中工会的所有成员都具有相同的 items
(或其他)属性:
type OrBranch = {
type: "or";
items: Branch[];
};
type AndBranch = {
type: "and";
items: Branch[];
};
这样,不关心分支类型的代码就可以使用 items
。您显示的代码是:
const items = _.reduce(branch.items, (all, item) => {
return _.union(all, item.items);
}, [] as Branch[]);
const retval = {type: branch.type, items};
让属性做 double-duty(指示分支的类型和其中的项)就像它们在当前类型中所做的那样,这使得编写类型安全代码来处理没有 knowing/caring 什么类型的项分支真的很难。
我有以下类型:
type OrBranch = {
or: Branch[]
}
type AndBranch = {
and: Branch[]
}
我想要一个 Branch
类型,它可以是 OrBranch
或 AndBranch
。所以我首先尝试:
type Branch = AndBrand | OrBranch
工作很棒,除非我想做类似的事情:
let branch: Branch = ...
let andOr = 'and'; // or 'or'
let nodes = branch[andOr]
然后我发现 branch
不可索引。好的,所以我尝试使用可索引类型:
type AndOr = 'and' | 'or';
type Branch = Record<AndOr, Branch[]>;
但这需要 and
和 or
都存在,所以在这种情况下我不能将 AndBranch 转换为 Branch。
同样
type Branch = Record<AndOr, Branch[]> | AndBranch | OrBranch
不起作用,原因相同。
虽然我可以使用类型保护来确定类型,但我有对这些对象进行操作的长函数,在这些对象中,除了 属性 之外,它们可以被同等对待。我希望通过使用 andOr
变量来消除一堆重复代码,类型保护并不能真正阻止。例如:
let retval = {} as Branch;
if (isAnd(branch)) { // branch is a Branch parameter passed in
(retval as AndBranch).and = [] as Branch[];
set = (retval as AndBranch).and;
} else {
(retval as OrBranch).or = [] as Branch[];
set = (retval as OrBranch).or;
}
set = _.reduce(set, (all, item: Branch)=> {
if (isAnd(branch) && isAnd(item))
return _.union(all, item.and);
else if (isOr(branch) && isOr(item))
return _.union(all, item.or);
else
return all;
}, [] as Branch[]);
对比
andOr = isAnd(branch) ? 'and' : 'or';
let retval = {} as Branch;
retval[andOr] = _.reduce(set, (all, item: Branch) => {
if (item[andOr])
return _.union(all, item[andOr]);
else
return all;
}, [] as Branch[]);
我知道有一种方法可以精确地要求 and
和 or
之一(就像对
是否可以同时获得两种效果?
使类型可索引并不能解决基本问题,即您试图在可能是 and
分支的对象上使用 属性 and
(因此有 属性) 或者可能是 or
分支(因此不是)。相反,询问分支它有什么并使用它,因为这允许 TypeScript 缩小类型:
if ("and" in branch) {
// ...use `branch.and`...
} else {
// ...use `branch.or`...
}
如果你的代码认为它知道分支是什么,你可以将它与你的 andOr
结合起来,也许通过使用类型断言函数:
function assertIsAndBranch(branch: Branch): asserts branch is AndBranch {
if (!("and" in branch)) {
throw new Error(`branch is not an AndBranch`);
}
}
// (And `assertIsOrBranch`)
然后:
if (andOr === "and") {
assertIsAndBranch(branch);
// ...here, TypeScript knows `branch` is an `AndBranch`...
}
重新编辑:如果您有重要代码需要在不知情的情况下处理 AndBranch
的 and
或 OrBranch
的 or
中包含的项目或者关心它们是 AndBranch
还是 OrBranch
实例,我将它们重新设计为 discriminated union,其中工会的所有成员都具有相同的 items
(或其他)属性:
type OrBranch = {
type: "or";
items: Branch[];
};
type AndBranch = {
type: "and";
items: Branch[];
};
这样,不关心分支类型的代码就可以使用 items
。您显示的代码是:
const items = _.reduce(branch.items, (all, item) => {
return _.union(all, item.items);
}, [] as Branch[]);
const retval = {type: branch.type, items};
让属性做 double-duty(指示分支的类型和其中的项)就像它们在当前类型中所做的那样,这使得编写类型安全代码来处理没有 knowing/caring 什么类型的项分支真的很难。