为创建动态类的函数添加 JSDoc 返回类型

问题描述 投票:0回答:1

有没有办法使用 JSDoc 定义这样的函数的返回类型,它创建一个扩展用户提供的类作为函数参数的类?

/**
 * @param {class} UserProvidedClass
 * @returns ???
 */
function createClientClass(UserProvidedClass) {
  return class ClientClass extends UserProvidedClass {
    bindServerSocket(socket) {
      this.server = socket;
      this.onConnect();
    }
    onConnect() {
      super.onConnect?.();
      if (DEBUG) console.log(`client connected`);
    }  
    ...
  };
}

因为这可以工作,但如果 UserProvidedClass 不“覆盖”它们,当然不会包含任何动态类的方法和字段:

/**
 * @param {class} UserProvidedClass
 * @returns {UserProvidedClass} 
 */
function createClientClass(UserProvidedClass) {
  return class ClientClass extends UserProvidedClass {
    bindServerSocket(socket) {
      this.server = socket;
      this.onConnect();
    }
    onConnect() {
      super.onConnect?.();
      if (DEBUG) console.log(`client connected`);
    }  
    ...
  };
}

替代方案似乎“被破坏”:如果我们将动态类作为真正的类取出,那么我们就会遇到继承问题:

class BaseClass {
    bindServerSocket(socket) {
      this.server = socket;
      this.onConnect();
    }
    onConnect() {
      super.onConnect?.();
      if (DEBUG) console.log(`client connected`);
    }
    ...
}

/**
 * @param {class} UserProvidedClass
 * @returns {T extends BaseClass, UserProvidedClass} JS cannot do this
 */
function createClientClass(UserProvidedClass) {
  return class ClientClass extends ...something JS can't do {
    ...
  };
}

这是 JSDoc 无法做到的事情之一,还是有一种 JSDoc 方法可以完成 TS 对类型和接口所做的事情,而无需编写“无操作”JS 来捕获某些类的方法和属性,而无需编写除了类型文档之外,你还用过其他东西吗?

按照建议,我阅读了关于 genericstemplates 的文档,但要么我跳过了某些内容,要么给出的示例未涵盖这一点,因为使用模板我能想出的所有内容都会产生返回类型类型提示是:

/**
 * @template T
 * @template { bindServerSocket(socket:WebSocket):void,onConnect():void } U
 * @param {T} UserProvidedClass
 * @returns {V implements U extends T}
 */
function createClientClass(UserProvidedClass) {
  return class ClientClass extends UserProvidedClass {
    bindServerSocket(socket) {
      this.server = socket;
      this.onConnect();
    }
    onConnect() {
      super.onConnect?.();
      console.log(`connected`);
    }
  };
}

const ClientClass = createClientClass(
  class {
    test() {
      console.log(`test`);
    }
  }
);

const client = new ClientClass();
client.bindServerSocket(new WebSocket(`http://localhost`));
client.test();

函数的返回类型现在标记为

implements U extends T
ClientClass
的键入提示为
ClientClass
,虽然
bindServerSocket
解析为动态类方法,但
test
不解析为传递给函数的类。

(同样的情况也发生在

@return {U extends T}
上,其中输入可以解析来自 U 的函数,但不能解析来自 T 的函数)

javascript jsdoc
1个回答
0
投票

一种方法是使用

@template
注解来指定泛型类型参数
T
,它表示调用者提供的基类。然后,您可以使用
@extends
关键字后跟模板参数名称 (
T
) 来指示返回的类应扩展指定的基类。最后,您可以使用
@returns
注释来指定函数的返回类型,这将是扩展基类的新创建的类。这是一个例子:

/**
 * Creates a new class that extends the provided base class.
 *
 * @template T - The base class to extend.
 * @returns A new class that extends {@linkcode T}.
 */
function createClientClass(baseClass) {
  // Implementation details omitted...
}

在此示例中,

@template
注释指定该函数采用名为
T
的单个类型参数。该类型参数表示调用者想要扩展的基类。
@returns
注解表示函数返回类型为
A
的值,其中
A
定义如下:

type A<T> = {
  new(...args: any[]): T & {
    bindServerSocket(socket: WebSocket): void;
    onConnect(): void;
  };
};

此类型定义使用 TypeScript 语法来表示构造函数,该函数接受任意数量的参数并返回扩展类的实例。

&
运算符用于将基类的类型与扩展添加的附加成员相交。在这种情况下,附加成员是
bindServerSocket()
onConnect()

要在代码中使用此类型定义,您需要在项目中启用 TypeScript 支持。如果您使用的是 Visual Studio Code,则可以将以下行添加到您的

tsconfig.json
文件中:

{
  "compilerOptions": {
    "target": "es6",
    "moduleResolution": "node",
    "strict": true,
    "jsx": "react"
  },
  "include": ["src"]
}

启用 TypeScript 支持后,您可以导入

createClientClass()
函数并使用基类作为其参数来调用它:

import { createClientClass } from './createClientClass';

// Create a new class that extends the 'BaseClass' class.
const ExtendedClass = createClientClass(BaseClass);

// Instantiate the extended class.
const instance = new ExtendedClass('foo', 'bar');

// Call the 'bindServerSocket()' method on the instance.
instance.bindServerSocket(new WebSocket('ws://example.com'));
© www.soinside.com 2019 - 2024. All rights reserved.