TypeScript的嵌套结构中是否可以具有变化的类型参数?

时间:2019-02-15 19:50:42

标签: javascript typescript

与该问题相对应的一个简单示例“一个简单的通用类型且类型安全的LinkedList之类的数据结构”,其中每个下一个Node可以具有不同的类型。例如,目的是实现这一点:

     interface LinkedNode<T1> {
        value: T1;
        next: LinkedNode<T2> | null; // Typescript gives error for T2 here.
      }

Use case:
      interface IPair{
        a:boolean;
        b: number;
      }
      let LinkedNode1: LinkedNode<string> = {value: "string1", next: null};
      let LinkedNode2: LinkedNode<number> = {value: 10, next: LinkedNode1};
      let LinkedNode3: LinkedNode<IPair> = {value: {a:true, b: 2}, next: LinkedNode2};
      let root: LinkedNode<boolean> = {value: true, next: LinkedNode3};

      let x = root.next.value.a; // ultimately this is the desired effect: to be able to get access to other linked node's member variables without having to manually typecast.

因此,问题是:“对于上面的LinkedNode<T1>接口,用<T2>替换<any>会编译,但是会丢失类型信息。是否可以在不使用的情况下实现上述预期的效果用<any>代替<T2>?如果不是,那还有什么选择?”

1 个答案:

答案 0 :(得分:3)

如果我对您的理解正确,那么您真的想要一种tuple类型的元组,其中元组中的每个元素都向下一层向下进入您的树。或至少这对我来说唯一有意义,因为您必须根据所有后代的类型来定义根节点的类型。

给出几个tuple-related features added in TypeScript 3.0,您可以执行以下操作:

// get the first element of a tuple; Head<[1,2,3]> is 1
type Head<L extends any[]> = 
  ((...l: L) => void) extends ((h: infer H, ...t: infer T) => void) ? H : never;

// get the tuple with the first element removed; Tail<[1,2,3]> is [2,3]
type Tail<L extends any[]> = 
  ((...l: L) => void) extends ((h: infer H, ...t: infer T) => void) ? T : never;

// represent LinkedNode in the following nested way
interface LinkedNode<T extends any[]> {
  value: Head<T>;
  next: LinkedNode<Tail<T>> | null; 
}

当您到达元组的末尾时,会有一些有趣的事情(LinkedNode<[]>LinkedNode<never>是有趣的类型),但是| null足以掩盖它。

让我们尝试一下:

const node: LinkedNode<[string, number, boolean]> = {
  value: "a",
  next: {
    value: 1,
    next: {
      value: true,
      next: null
    }
  }
};

let LinkedNode1: LinkedNode<[string]> = { value: "string1", next: null };
let LinkedNode2: LinkedNode<[number, string]> = { value: 10, next: LinkedNode1 };
let LinkedNode3: LinkedNode<[IPair, number, string]> = { value: { a: true, b: 2 }, next: LinkedNode2 };
let root: LinkedNode<[boolean, IPair, number, string]> = { value: true, next: LinkedNode3 };

对我很好。


在TS3.0之前,您可以获得带有嵌套泛型的东西,这可能更简单:

interface LinkedNode<T, N extends null | LinkedNode<any, any>=null> {
  value: T;
  next: N;
}

const node: LinkedNode<string, LinkedNode<number, LinkedNode<boolean>>> = {
  value: "a",
    next: {
    value: 1,
      next: {
      value: true,
        next: null
    }
  }
};

let LinkedNode1: LinkedNode<string> = {value: "string1", next: null};
let LinkedNode2: LinkedNode<number, typeof LinkedNode1> = {value: 10, next: LinkedNode1};
let LinkedNode3: LinkedNode<IPair, typeof LinkedNode2> = {value: {a:true, b: 2}, next: LinkedNode2};
let root: LinkedNode<boolean, typeof LinkedNode3> = {value: true, next: LinkedNode3};

看起来也不错。这些帮助之一吗?