如何推导出哪种类型与泛型匹配?

How to derive which type matches a generic?

给定以下类型……

interface ParentNode {
    items: Node[]
}

interface LeafNode {
}

type Node = ParentNode | LeafNode

function List<Nodes extends unknown[]>(items: Nodes)

…我如何得出传递给 Nodesunknown 类型是否匹配 ParentNode 类型(即具有 items 属性)或 LeafNode类型?

interface A1 {
    items: []
}

interface A2 {
    name: string;
}

List<A1|A2>([
    { name: 'Hello' },
    { items: [
          { name: 'Bye' }
          { items: [] }
      ] }
])

// in the List function

if ('items' in item) {
    let typedItem = item as A1 // desired effect but I'm aware it's not possible like this
} else {
    let typedItem = item as A2 // desired effect but I'm aware it's not possible like this
}

即使 List 函数不知道哪些类型 Nodes 可以提前,我该如何做到这一点?

如果我没理解错的话,我会在界面中添加一个属性,比如type,以辅助处理:

interface Base {
  type: 'foo' | 'bar';
  // ... some other common attr
}

// Leaf
interface Bar extends Base {
  type: 'bar';
  name: string;
  // attrs?: any[];
  // items?: Bar[];
}

// Parent
interface Foo extends Base {
  type: 'foo';
  items: Bar[];
}

type Baz = Foo | Bar; // no `Base` here

function isBaz(item: unknown): item is Baz {
  if (typeof item !== 'object' || item === null) {
    return false;
  }

  if (!('type' in item)) {
    return false;
  }

  if (!('items' in item) && !('name' in item)) {
    return false;
  }

  // more checks
  return true;
}

function List<T extends unknown = unknown>(items: T[]) {
  // items is array of anything
  items.forEach((item) => {
    if (isBaz(item)) {
      // item is now T & Baz
      if (item.type === 'foo') {
        // item should be `Foo` here
        console.log(item.items); // []
        return;
      }
  
      // item should be `Bar` here
      console.log(item.name); // Hello
    }
  });
}

List([
  {
    type: 'bar',
    name: 'Hello',
  },
  {
    type: 'foo',
    items: [],
  },
]);