TypeScript 导入的JSDoc等效项 *

yquaqz18  于 2022-10-29  发布在  TypeScript
关注(0)|答案(9)|浏览(200)

检索词

  • jsdoc导入
  • 汇入 *
  • 导入命名空间
  • jsdoc命名空间

建议

提供import * as Sub from "./sub"的JSDoc等效项,以便./sub模块中的类型和接口可以引用为Sub.TypeSub.Interface
我对实际的语法没有偏好,但根据我的经验,通常期望以下语法能够做到这一点(但事实并非如此,而且我似乎无法理解Sub在这里会产生什么结果:

@typedef {import('./sub')} Sub

如果以上不是可行的选项,也许使用相同的*字符可以是一个选项:

@typedef {import('./sub').*} Sub

或者可能是全新的@import jsdoc标记。

用例

js-ipfs(相当大的代码库)已经通过JSDoc语法采用了TS,但是处理大量从其他模块导入的类型和接口是一个主要的痛苦来源,原因如下:
1.每一种类型都需要@typedef {import('...').Name} Alias
1.导入泛型类型/接口需要通过@template标记重复类型参数

  • 这很容易出错,因为如果省略它,它将变成any
  • 最后需要大量打字
  • 对库布局的更改需要更新整个代码库中的所有类型定义(很遗憾,vscode在这方面帮不上忙)
  • 另一种方法是将它们合并到一个地方,但是vscode(和类似的)需要多次跳转/单击才能到达实际的类型定义。

以上所有这些都使原本大多是好的经历变成了痛苦。
目前的方法还有一个副作用,就是将导入的接口转换为类型(参见#41258),而这些类型不能在implements语法中使用,因为它们不再是接口。
假设可以执行import * as Sub from "./sub"以获得TS语法中的导出的命名空间,则在jsdoc语法中似乎等效于:
1.允许减少@typedef {import(...).Name} Name声明的数量。
1.允许导入泛型类型,而不必通过@temaplate标记重新键入类型参数。
1.避免转动接口。
示例
考虑以下TS代码:

import { BlockEncoder, BlockDecoder } from "@multiformats/codecs"
import { DagNode } form "./node"

class DagPB implements BlockEncoder<0x70, DagNode>, BlockDecoder<0x70, DagNode> {
  async encode(node:DagNode):Promise<Uint8Array> {
     // ...
  }
  async decode(bytes:Uint8Array):Promise<DagNode> {
    // ...
  }
}

使用JSDoc语法的相同代码变为以下代码:

/**

* @template {number} Code
* @template T
* @typedef {import('@multiformats/codecs').BlockEncoder<Code, T>} BlockEncoder
* /

/**

* @template {number} Code
* @template T
* @typedef {import('@multiformats/codecs').BlockDecoder<Code, T>} BlockDecoder
* /

/**

* @typedef {import("./node").DagNode} DagNode
* /

/**

* @implements {BlockEncoder<0x70, DagNode>}
* @implements {BlockDecoder<0x70, DagNode>}
* /

class DagPB {
  /**

* @param {DagNode} node
* @returns {Promise<Uint8Array>}
* /

  async encode(node) {
     // ...
  }
  /**

* @param {Uint8Array} bytes
* @returns {Promise<DagNode>}
* /

  async decode(bytes) {
    // ...
  }
}

注意:由于#41258,上述代码甚至不会生成所需的typedef

现在,如果我们有这个问题的建议,它将是:

/**

* @typedef {import('@multiformats/codecs')} Codec
* @typedef {import('./node').DagNode} DagNode
* /

/**

* @implements {Codec.BlockEncoder<0x70, DagNode>}
* @implements {Codec.BlockDecoder<0x70, DagNode>}
* /

class DagPB {
  /**

* @param {DagNode} node
* @returns {Promise<Uint8Array>}
* /

  async encode(node) {
     // ...
  }
  /**

* @param {Uint8Array} bytes
* @returns {Promise<DagNode>}
* /

  async decode(bytes) {
    // ...
  }
}

检查清单

我的建议符合以下准则:

  • 这不会是对现有TypeScript/JavaScript代码的重大更改
  • 这不会改变现有JavaScript代码的运行时行为
  • 这可以在不基于表达式的类型发出不同JS的情况下实现
  • 这不是运行时功能(例如,库功能、带有JavaScript输出的非ECMAScript语法等)
  • 这个特性将与TypeScript's Design Goals的其余部分一致。
toe95027

toe950271#

cc @sandersn,用于查看能见度

whitzsjs

whitzsjs2#

今天遇到了类似的相关问题
我尝试执行以下操作

/**@typedef {import('../../types.js')} T */

/**

* @return {Promise<T.Result<any>>}
* /

module.exports = async (args) => { ... }

其中,types.js

/**
 * @template T
 * @typedef {{
 *    err: Error,
 *    data?: undefined
 * } | {
 *    err?: undefined,
 *    data: T
 * }} Result<T>
 */

module.exports = {}

然后我得到了
'T'仅指涉型别,但在这里是当做命名空间使用。
我想把整个包当作一个命名空间,这样我就可以导入它,并使用import()函数语法引用其中的类型,但看起来这只返回类型,而不返回命名空间。

m528fe3b

m528fe3b3#

@哥扎拉
由于随机愚蠢的运气和只是尝试的东西,我发现你可以在一个项目(www.example.com)declare ...https://github.com/voxpelli/types-in-js/blob/main/FAQ.md#i-want-to-share-some-helper-interfaces-or-aliases-globally-in-my-jsdoc-project。
您可以声明@multiformats/codecs/declare.d.ts中存在各种类型,然后将其包含在jsconfig/tsconfig中,以便这些标记在全局范围内可用。
您也可以使用命名空间来namespace codecs { interface BlockEncoder }
这并不能解决任何导入用例,但是事实证明,编写导出的作者可以导出到一个全局名称空间,该名称空间可以在任何地方使用,而无需使用import语法。

biswetbf

biswetbf4#

可怜的男人的解决方案我已经确定是沿着这些路线:

lib.js(实作位于此处)

import * as API from "./result.js"
/**

* @template T
* @param {T} value
* @returns {API.Result<never, T>}
* /

export ok = (value) => ({ ok: true, value })

result.js(只是lib.js的一个外观)

export * from "./lib.js"

result.ts(阴影.js以添加类型)

export * from "./lib.js"
export type Result<X, T> =
  | { ok: true, value: T }
  | { ok: false, error: X }

现在,任何其他模块都可以执行以下操作:

import * as Result form "../path/to/result.js"

/**

* @template X, T
* @param {Result.Result<X, T>} result
* @param {(t:T) => U} f
* @returns {Result.Result<X, U>}
* /

export const map = (result, f) =>
  result.ok ? Result.ok(f(result.value)) : result
gojuced7

gojuced75#

我想澄清一下问题的实质,主要是:

/**

* @typedef {import("./someModule.js")} SomeModule
* /

实际上相当于:

/**

* @typedef {typeof import("./someModule.js")} SomeModule
* /

但是,在TypeScript中,以下内容通常是不同的:

import type * as SomeModule from "./someModule.js";
type SomeModule = typeof import("./someModule.js");
px9o7tmv

px9o7tmv6#

嘿,队员们,
我一直在引入外观文件来解决每次导入时重新输入所有泛型的问题,但我确实觉得需要一个合适的解决方案。我还遇到了#22160,它似乎要求使用不同的语法来获得相同的特性。

at0kjp5o

at0kjp5o7#

不确定是否相关,但似乎@typedef并没有真正注册为类型符号
这会影响构造函数,使我无法看到构造函数上的签名和自动完成

pftdvrlh

pftdvrlh8#

@RyanCavanaugh@sandersn对此有什么更新吗?这可能是在使用带有JSDoc的TS时最烦人的限制。
如果有人愿意在这方面指导我,我会把我的时间放在这上面。

vsnjm48y

vsnjm48y9#

编辑:这似乎不是我在前面的评论中所说的情况
我的问题实际上是@typedef没有按预期工作
使用@type是我所期望的

但是@typedef没有

使用@var也不是解决方案

相关问题