首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何递归使用Typescript泛型?

如何递归使用Typescript泛型?
EN

Stack Overflow用户
提问于 2018-08-29 22:01:43
回答 2查看 2.8K关注 0票数 4

我在使用递归泛型时遇到了问题。

我使用泛型创建了一个接口。这个想法是一个分支可以有许多子分支,这些子分支也是泛型。这是我的代码(我将其删掉以供发布)

代码语言:javascript
复制
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分支的子级时,typescript抛出以下错误;

Type 'Branch<Contact>[]' is not assignable to type 'Branch<Office>[]'...

这样做的原因是因为分支的类型是未知的,如果可以避免的话,我也不想使用any

那么,我该如何解决这个问题呢?

EN

回答 2

Stack Overflow用户

发布于 2018-08-30 00:36:23

因此,如果您确实想要强类型Branch,则需要为其提供一个与所有children嵌套级别相对应的类型。这看起来像一个类型的列表或元组。自从TypeScript 3.0引入了tuple types in rest/spread expressions以来,你可以在某种程度上表达这一点,但我不知道这对你来说是否值得。

首先,让我们定义类型函数HeadTail,它们将元组类型拆分为第一个元素和其余元素的元组:

代码语言:javascript
复制
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]

现在我们可以定义BranchInterfaceBranch来接受一个类型元组,如下所示:

代码语言:javascript
复制
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],并看看它是否有效:

代码语言:javascript
复制
const rootBranch = new Branch<[Office, Contact]>(newOffice);
const anOffice = rootBranch.record; // Office
const aContact = rootBranch.children[0].record; // Contact

当然,如果您遍历了这些内容,就会发现Head<[]>是什么(我猜该实现提供了{} ):

代码语言:javascript
复制
const whoKnows = rootBranch.children[0].children[0].record; // {}

如果你想让Contact下面的层类似于never (因为你永远不会遍历那么远),你可以使用这样的rest tuple

代码语言:javascript
复制
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

请注意,这要求您在构造Branch时显式指定类型参数T,因为编译器无法从参数推断类型:

代码语言:javascript
复制
const oops = new Branch(newOffice); 
oops.record; // any, not Office

好吧,它起作用了。如果你想走那条路,由你决定。希望这能有所帮助;祝你好运!

票数 2
EN

Stack Overflow用户

发布于 2018-08-29 22:57:39

看起来你可能正在使用泛型,简单的继承就可以了。如果您所需要的只是让一个办公室或一个联系人分支拥有一个分支子分支的列表,那么下面的代码只使用一个泛型数组就可以工作。

代码语言:javascript
复制
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个泛型类型来分离记录类型和子类型,如下所示:

代码语言:javascript
复制
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>();
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/52079301

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档