泛型可以在不扩展的情况下具有几种特定类型的约束吗?

时间:2019-04-29 19:28:56

标签: typescript

我的目标是利用严格的类型输入来确保对象正确格式化。我希望能够指定一些必须遵循的有效格式并强制执行。

interface TypeA { A: void; }
interface TypeB { B: void; }
interface TypeC { C: void; }

type Type = TypeA | TypeB | TypeC;

interface BaseItem<T extends Type> { name: string; data: T; }

type Item = BaseItem<TypeA> | BaseItem<TypeB> | BaseItem<TypeC>;

const collection: Item[] = [
  { name: 'A', data: { A: null } },
  { name: 'B', data: { B: null } },
  { name: 'C', data: { C: null } },
];

class Example<T extends Type> {
  item: BaseItem<T>;

  add(item: BaseItem<T>) {
    this.item = item;
    collection.push(item); // Error on `item`

    /**
     * Argument of type 'BaseItem<T>' is not assignable to parameter of type 'Item'.
     *   Type 'BaseItem<T>' is not assignable to type 'BaseItem<TypeA>'.
     *     Type 'T' is not assignable to 'TypeA'.
     *       Type 'Type' is not assignable to type 'TypeA'.
     *         Property 'A' is missing in the type 'TypeB' but required in type 'TypeA'.
     */
  }
}

在上面的代码中,类型Item用于强制collection数组中对象的格式。这给出了我计划如何使用这种格式的想法。

也在上面的代码中,我尝试对Example类使用泛型。我的想法是,我可能想要类的几个属性来确保它们在任何给定时刻都使用共享的泛型。而且尽管通用扩展了有效类型,但据我所知,它在理论上可以支持超出其类型的类型(例如BaseItem<TypeA> & { more: string })。

我了解为什么它不能在当前状态下工作。我不明白的是我将如何实现自己想要的。

有没有一种方法可以使用泛型来严格匹配某种类型的并集,而不是扩展某种类型的并集?就像<T extends Type>一样,不是<T is Type>吗?还是有其他方法可以解决这个问题?

1 个答案:

答案 0 :(得分:1)

我不确定如何对此进行分类,这是一个限制吗?不过,对我来说,这似乎有点麻烦。解决方法如下:

interface TypeA { A: void; }
interface TypeB { B: void; }
interface TypeC { C: void; }

type Type = TypeA | TypeB | TypeC;

interface BaseItem<T extends Type> { name: string; data: T; }

type Item = BaseItem<TypeA> | BaseItem<TypeB> | BaseItem<TypeC>;

const collection: Item[] = [
  { name: 'A', data: { A: null } },
  { name: 'B', data: { B: null } },
  { name: 'C', data: { C: null } },
];

class Example<T extends Type> {
  item: BaseItem<T>;

  // notice this conditional type, see the irony here?
  // everything `extends any`, so this expression reduce to
  // just `BaseItem<T>`, never `never`, so why the trouble?
  add(item: T extends any ? BaseItem<T> : never) {
    this.item = item;
    collection.push(item);  // cus this way error magically disappear :)
  }
}

使用TS时涉及的所有技巧我都有点累。这是distributive conditional types,是的,还是另一个花哨的名字,如果您有兴趣的话,请单击。我个人不知道如何证明这种行为。