【问题标题】:Is it possible to enforce type-safety of generic abstract methods without declared superclass generics?是否可以在没有声明超类泛型的情况下强制泛型抽象方法的类型安全?
【发布时间】:2023-03-30 22:47:01
【问题描述】:

我有一个抽象类:

public abstract class ParentClass<T, U> {
    // a bunch of common code to prevent duplication in children
    protected abstract adaptThese(...things: Array<MyType>): Array<T>
    protected abstract adaptThose(...things: Array<MyType>): Array<U>
    // some other non-generic abstract methods
}

这是一个示例子类:

public class ChildClass extends ParentClass<FirstType, SecondType> {
    protected adaptThese(...things: Array<MyType>): Array<FirstType> {
       //...
    }
    protected adaptThose(...things: Array<MyType>): Array<SecondType> {
       //...
    }
    // implementations of the other abstract methods...
}

我的代码按原样工作,但问题是所有引用ParentClass 的代码都必须将其引用为ParentClass&lt;any, any&gt;。由于泛型类型纯粹用于内部目的(仅限protected 的东西),我想知道是否有一种好方法可以从父类中删除泛型类型,同时仍然为孩子们使用&lt;T&gt; 和@ 强制执行类型安全987654327@.

我最初引入这些泛型是为了强制子类提供从MyTypeTU 的某种适应,它们是依赖于库的,从不提供MyType。目标是允许我的项目的其余部分使用MyType,无论使用哪个底层库,因为它们在设计上都非常相似,但接受的类型不同。这将允许在不影响此层次结构之外的代码的情况下换入或换出所选包。 (我使用的是 Angular,所以这意味着只需将提供商的 useClass 更改为一行)。

我试图制作某种可以包含泛型的内部适配器对象,但我不知道如何强制子类声明和实现类似于上面的方法。

我得到的最接近的方法是在ParentClassMyAdapter&lt;any, any&gt; 类型的构造函数中添加一个参数,但我无法设法让any 成为一些在孩子们。

可能真正有用的一条信息是ParentClass 从不引用泛型方法本身,因此不一定需要声明它们以供ParentClass 使用。他们只是在那里强迫孩子们实施它们。也许我可以使用某种界面设计来实现这一点?

简而言之:如果有的话,我如何强制子级实现类型安全的方法而不在父级的签名中声明类型?


请求了一个示例,因此这里是一个简化的示例,如何拆分类型依赖:

ParentClass,我可能有这样的方法:

// sets an array of things for a certain key (label)
setThingsFor(label: string) {
    const thingsForLabel: Array<MyType> = myConfiguration.get(label);
    // do some calculations, and filter things based on configuration
    this.setThings(thingsForDisplay);
}

// sets an array of things for the feature
// this is public because the things can be set while avoiding the above calculations and configurations
abstract setThings(things: Array<MyType>): void;

在子类中:

public class CoolLibraryImplForParentClass extends ParentClass<CoolType, RadType> {
    setThings(things: Array<MyType>): void {
        // use "things" to determine some library-specific config
        coolLibraryService.initialize(this.adaptThose(things)); // RadType
        coolLibraryService.doThings(this.adaptThese(things));   // CoolType
    }        

    // the generic methods and some CoolLibrary-specific things

}

有许多类似于上述的方法,用于工具或从库的服务中请求状态,需要这两种类型中的一种。

每个库的代码都非常相似,因此我为面向项目的代码引入了一个父类,并让子类使用依赖于库的细节。

此后我考虑的另一个问题是,我可能能够创建一个面向项目的服务类,然后为该类提供一个通用类型的库集成服务,但这是一个相当大的重写,我仍然对这个问题的答案。

【问题讨论】:

  • 使用提供的代码,我将完全删除 TU 并让父类返回 Array&lt;any&gt;。如果您有一些实际需要 TU 类型(或类似类型)的约束,那么也许您可以添加更多代码来显示这些依赖关系?
  • @jcalz 我添加了一个示例,感谢您的关注
  • 嗯,我仍然不明白ChildClass 是如何关心这两种特定类型的。我认为您至少需要展示两个不同的子类实现,以便有人可以看到相同之处和不同之处。在这一点上,我仍然会说您不需要 TU 并将其留给子类实现来使用他们想要的库。

标签: typescript generics


【解决方案1】:

处理此问题的最佳方法可能是创建一个通用接口IParentClass,然后在您之前引用ParentClass&lt;any, any&gt; 的任何地方引用IParentClass

interface IParentClass {
  // all the public methods from ParentClass
}

abstract class ParentClass<T, U> implements IParentClass {
  // same as before
}

// reference like this
var foo: IParentClass;

或者,您可以提供any 作为ParentClass 上泛型类型参数的默认值,但这并不理想,因为无法强制子类为泛型提供适当的值。例如(我引入了一些新的方法签名以使问题显而易见):

abstract class ParentClass<T = any, U = any> {
  protected abstract returnTs(): Array<T>
  protected abstract alsoReturnTs(): Array<T>
  // etc
}

// referencing like this compiles now
var foo: ParentClass;

// This one gets strong typing since it overrides the default `any` generic param type
class GoodChildClass extends ParentClass<string, number> {
  protected returnTs(): Array<string> {
    return ['hello'];
  }
  protected alsoReturnTs(): Array<string> {
    return ['world'];
  }
}

// This one doesn't!
class BadChildClass extends ParentClass {
  protected returnTs(): Array<number> {
    return [1];
  }
  protected alsoReturnTs(): Array<string> {
    return ['oh no'];
  }
}

【讨论】:

  • 谢谢。我认为,至少就目前而言,您的替代方案适合我当前的实现,因为孩子很少,而且泛型仍然鼓励新的实现来添加类型安全,尽管它不是必需的。
猜你喜欢
  • 1970-01-01
  • 2023-03-11
  • 2018-08-26
  • 1970-01-01
  • 1970-01-01
  • 2022-01-05
  • 2019-04-02
  • 2023-03-28
  • 2012-03-01
相关资源
最近更新 更多