特定 javascript 实例化模式的正确打字稿定义是啥

Posted

技术标签:

【中文标题】特定 javascript 实例化模式的正确打字稿定义是啥【英文标题】:What is the right typescript definition for a specific javascript instantiation pattern特定 javascript 实例化模式的正确打字稿定义是什么 【发布时间】:2019-04-22 03:36:28 【问题描述】:

我目前正在处理 Paper.js 库打字稿定义,但我无法找到正确的方法来记录 API 的某些部分。

这种情况可以简化为:假设我们有一个Animal 类,它有一个静态属性Dog,用作该类的自定义构造函数:

var Animal = function(type) ;
Animal.Dog = function() 
    return new Animal('dog');
;

Animal 实例可以通过两种方式构建:

var dog = new Animal('dog');

或者:

var dog = new Animal.Dog();

在这两种情况下,我都需要将dog 变量的类型推断为Animal


我第一次尝试:

declare class Animal

    constructor ( type )
    static Dog (): Animal

但是 TSLint 失败并出现错误:“只能使用 'new' 关键字调用 void 函数。”,因为 Animal.Dog() 函数返回类型是 Animal

如果我将Animal.Dog() 的返回类型设置为void

declare class Animal

    constructor ( type )
    static Dog (): void

TSLint 通过但我得到void 作为推断类型...


所以我尝试了另一种方法:

declare class Animal

    constructor ( type )


declare namespace Animal

    export class Dog extends Animal
    
        constructor()
    

这样,TSLint 通过但在以下情况下:

var dog = new Animal.Dog();

dog 变量的推断类型是 Animal.Dog,而不是我想要的 Animal

这不是什么大问题,因为Animal.Dog 类型扩展了Animal,但库中没有Animal.Dog,所以我发现这种解决方法会误导用户。

有没有人知道处理这种情况的更好方法?

编辑

详细说明@stramski 解决方案,我补充了这个问题,Animal.Dog 可以有多个签名(例如Animal.Dog()Animal.Dog(color)),我的目标是分别记录它们。

【问题讨论】:

最后一个例子,相信你可以通过var dog: Animal = new Animal.Dog();强制使用Animal类型 @dotconnor - 可以,但存在维护风险。您必须记住每次都这样做。 不使用new 关键字怎么办?或者如果你经常把 Dog 和 Animal 混为一谈,那会不会太混乱了? @dotconnor - 这是由 Paper.js 而不是 OP 决定的,例如 Shape.Circle。 (我个人认为记录您通过new 调用它的做法很糟糕,但他们确实......) @t-j-crowder,是的,这正是问题所在:)。我的目标是按原样为 API 创建类型定义。即使它的某些部分看起来很奇怪...... 【参考方案1】:

这样的事情怎么样:

declare class Animal

    constructor ( type )
    static Dog : (new () => Animal)

编辑

由于有重载的构造函数,所以打字有点不同:

declare class Animal

    constructor ( type )
    static Dog : (new () => Animal) & (new (color) => Animal)

【讨论】:

是的,它比我的实际解决方法要好得多,因为它不需要额外的命名空间声明!非常感谢。 对此进行实验,我发现我的问题比我最初要求的要广泛一些:在我的情况下,静态构造函数可以有多个签名,例如Shape.Circle(center, radius) 和 Shape.Circle(object)。你知道我如何将你的想法扩展到这个案例吗? 您可以将参数传递给构造函数。所以对于 Shape.Circle 方法,输入是: static Circle: (new (obj: object) => Shape) & (new (radius: number, center: number) => Shape); 这是完美的类型,谢谢。唯一的交易是所有签名只能有一个评论,但我想这是一个小问题。【参考方案2】:

由于您是子类化.. 保持简洁明了很重要。在上面写的方式中,您可以看到 Dog 被创造为动物,但它与动物没有区别。在某些情况下,有一些变量或方法被覆盖。话虽如此,我发现如果你实现类似的东西会更好:

class Animal 
  constructor()
  communicate()  return "Makes Noise"; 


class Dog extends Animal 
  constructor()
    super();
  

  communicate()  return "Barks"; 

您可以从那里重写方法或变量,以便正确区分 Dog 和 Animal 以及其他 Animal 子类。

【讨论】:

感谢您的回答,但我认为您误解了这个问题。关键是我必须为javascript 特定实现创建一个类型定义,该实现不使用子类化并且Dog 类没有意义。 Animal.Dog 只是一个静态方法,用于以快捷方式实例化 Animal 类。您提出的定义实际上不允许做var dog = new Animal.Dog(); 这是我想要做的...... 我试图为你理解这个问题,我没有看到没有子类化的要求,因为我上面的实现是 Dog IS-A Animal。由于您说动物是一个类,而不是一个容器,因此它暗示了子类化,因为会涉及方法,并且您不必将 1 个子定义耦合到父对象。一切都很好,np。

以上是关于特定 javascript 实例化模式的正确打字稿定义是啥的主要内容,如果未能解决你的问题,请参考以下文章

用值实例化打字稿类实例(对象初始化)

从打字稿的目录中实例化所有类

打字稿`可以用不同的约束子类型实例化`错误

打字稿装饰器与类继承

打字稿中的多个构造函数

在通用应用程序中实例化设备特定视图控制器的正确模式是啥?