【问题标题】:Can generics have a constraint of several specific types without extending?泛型可以在不扩展的情况下限制几种特定类型吗?
【发布时间】:2019-04-30 04:57:31
【问题描述】:

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

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&lt;TypeA&gt; &amp; { more: string })。

我明白为什么它在当前状态下不起作用。我不明白我将如何完成我想要的。

有没有办法使用泛型来严格匹配一种联合而不是扩展一种联合?比如,不是&lt;T extends Type&gt;,而是&lt;T is Type&gt;?或者,有没有其他方法可以解决这个问题?

【问题讨论】:

  • 您到底想解决什么问题?当前代码产生错误,因为BaseItemItem 的子类型,而不是相反。我不明白为什么Type 存在问题?
  • 如果你想要T is Type,那么从字面上看,你不需要一个通用的cus,你已经知道它是Type
  • 最终目标是使用T 类型使类具有多个属性和方法。这将允许任何子类更具体(例如class Specific extends Example&lt;TypeA&gt; { ... })并强制遵守指定的任何类型。
  • 如果我只是使用Type,那么我将无法确保TypeA 在应该只使用TypeA 的子类中使用超过TypeB .
  • 那么你已经得到了你想要的,&lt;T extends Type&gt; 就是你所说的&lt;T is Type&gt;。就像我说的,你得到的错误与T 无关

标签: typescript


【解决方案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,是的,又是一个花哨的名字,如果您有兴趣,请点击。我个人不知道如何为这种行为辩护。

【讨论】:

  • 我对 TS 如何处理泛型类型参数的当前行为不满意。即使有那么强的约束,也不能确定一切。
  • 该链接让我意识到我可以使用该技术为所有项目类型创建一个ItemByType&lt;T extends Type&gt; 分配条件类型,其值为T extends TypeA ? BaseItem&lt;TypeA&gt; : ...。这有点……冗长,但它有效。
  • @KOVIKO upvote/accept 如果对你有帮助会很好。
  • 我觉得我不能接受这个作为答案,因为我不确定在这种情况下使用 any 的含义。但是值得一票!
猜你喜欢
  • 2019-05-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多