我在使用递归泛型时遇到问题。
我使用泛型创建了一个接口。这个想法是一个分支可以有很多子分支,它们也是泛型的。这是我的代码(我将其删除以进行发布)
import Contact from 'Business/Models/Contact';
import Office from 'Business/Models/Office';
export interface BranchInterface<T> {
children: Array<BranchInterface<T>>;
record: T;
}
export class Branch<T> implements BranchInterface<T> {
public children: Array<BranchInterface<T>> = [];
constructor(public record: T) {}
}
const newOffice = new Office();
const rootBranch = new Branch<Office>(newOffice);
rootBranch.children = Array<Branch<Contact>>(); << is the issue
问题是当我构造一个Office
分支时,子数组被构造为Office
类型。
这意味着当我尝试分配Contact
分支的数组时,Office
分支打字稿的子项会引发以下错误;
Type 'Branch<Contact>[]' is not assignable to type 'Branch<Office>[]'...
之所以这样做,是因为分支的类型是未知的,如果可以避免的话,我也不想使用any
。
那么,我该如何解决?
答案 0 :(得分:0)
似乎您可能在使用泛型来实现简单继承。如果您只需要办公室或联系人分支具有分支孩子的列表,则以下代码仅适用于通用数组。
export class Branch {
children: Array<Branch>;
constructor(){
this.children = new Array<Branch>();
}
}
class Office extends Branch {}
class Contact extends Branch {}
const newOffice = new Office();
newOffice.children.push(new Contact());
但是,如果您打算使用泛型,则需要在Branch类中使用2个泛型类型来分离记录和子类型,例如:
export class Branch<T, U> {
public children: U[];
constructor(public record: T) {
this.children = [];
}
}
class Office {};
class Contact {};
const newOffice = new Office();
const rootBranch = new Branch<Office, Contact>(newOffice);
rootBranch.children = new Array<Contact>();
答案 1 :(得分:0)
因此,如果您真的想强行键入Branch
,则需要为其提供一种与children
的所有嵌套级别相对应的类型。这看起来像是类型的列表或元组。由于TypeScript 3.0引入了tuple types in rest/spread expressions,因此您可以表达这一点,但是我不知道这是否值得。
首先,让我们定义类型函数Head
和Tail
,它们将元组类型分为其第一个元素和其余元素的元组:
type HeadTail<L extends any[]> =
((...args: L) => void) extends ((x: infer H, ...args: infer T) => void) ? [H,T] : never
type Head<L extends any[]> = HeadTail<L>[0];
// e.g., Head<[string, number, boolean]> is string
type Tail<L extends any[]> = HeadTail<L>[1];
// e.g., Tail<[string, number, boolean]> is [number, boolean]
现在,我们可以定义BranchInterface
或Branch
来接受如下类型的元组:
export interface BranchInterface<T extends any[]> {
children: Array<BranchInterface<Tail<T>>>
record: Head<T>;
}
export class Branch<T extends any[]> {
public children: Array<BranchInterface<Tail<T>>> = [];
constructor(public record: Head<T>) { }
}
假设您希望将顶层设为Office
,将下一层设为Contact
,则可以将类型列表定义为[Office, Contact]
并查看如果可行:
const rootBranch = new Branch<[Office, Contact]>(newOffice);
const anOffice = rootBranch.record; // Office
const aContact = rootBranch.children[0].record; // Contact
当然,如果您经过这一点,就会发现Head<[]>
是什么(我想该实现会提供{}
):
const whoKnows = rootBranch.children[0].children[0].record; // {}
如果要将Contact
下面的图层改成never
(因为您将永远不会向下走那么远),则可以使用rest tuple,如下所示:>
const rootBranch = new Branch<[Office, Contact, ...never[]]>(newOffice);
const anOffice = rootBranch.record; // Office
const aContact = rootBranch.children[0].record; // Contact
const aNever = rootBranch.children[0].children[0].record; // never
const anotherNever = rootBranch.children[0].children[0].children[0].record; // never
请注意,这需要您在构造T
时显式指定类型参数Branch
,因为编译器无法从参数推断类型:
const oops = new Branch(newOffice);
oops.record; // any, not Office
嗯,它有效。如果您想那样做,则取决于您。希望能有所帮助;祝你好运!