如何在 JSDoc 中传递通用类型参数?

14

在放弃之前,我想在这里试一试。

我有一个包含以下类型定义的定义文件:

/**
 * My json decode function, in reality very different
 * implementation (class like) but it works as an example
 */
  function decodeJSON<T = unknown>(str: string): T;

如果我想在TypeScript中使用这个泛型,我会做以下操作:
const value = decodeJSON<number[]>("[1,2,3]"); // return type will be number[]

然而,在我的实际代码中,我不能使用TypeScript,只能用它的类型定义来进行开发,并且我似乎找不到一种方法来告诉我的编辑器我传递给泛型参数的类型是什么,以便获得正确的类型信息。

我尝试使用JSDoc来指定泛型参数的参数可能与TypeScript相同的方式

// I was expecting JSDoc to understand something like this, but it doesn't
/** @type {decodeJSON<number[]>} */
const value = decodeJSON("[1,2,3]"); // Type inference will be unknown

但是它并不起作用。我并不在乎运行时的结果可能是什么,因为我已经实施了健全性检查。我只想让我的编辑器给我关于通用函数结果的类型提示,以使我的工作(这个函数在我的应用程序中有很重要的用途)更加容易。
我的编辑器是WebStorm,如果有影响的话,但这似乎是JSDoc的一般限制。

这个有帮助吗?(https://github.com/microsoft/tsdoc/issues/72) - Jared Smith
1
你的编辑器环境应该已经能够满足你的需求,如果它有良好的 TypeScript 支持的话。即使是在线的 TypeScript Playground,在悬停泛型参数类型时也会提供工具提示。在这里悬停函数使用此处你应该会看到这个。你还需要其他什么帮助吗? - Alex Wayne
1
@AlexWayne 噢,我对TypeScript的定义没有问题,它完全正常。问题出现在我需要实际使用泛型函数时。由于我不再使用TypeScript而是使用带有定义的纯JavaScript,所以我无法执行decodeJSON<theGenericType>(),因此编辑器会告诉我函数结果的类型未知。 - Steven Guerrero
2
@AlexWayne 好的,TypeScript可以使用JSDoc中的@template,基本上与泛型相同。我不认为这是一个不可能的请求。 - Steven Guerrero
错误信息为 Type 'X' is not generic.ts(2315),而明显期望的是 /** @type {decodeJSON<number[]>} */ - milahu
显示剩余10条评论
7个回答

13

3
如果有人在这里搜索如何使用JsDoc输入ts泛型 ts Generic <T>,我想提供一些建议。
这是ts泛型。
function identity<T>(arg: T): T {
  return arg;
}

可以实现为:
/**
 * @template T
 * @param {T} arg
 * @return {T}
 */
function identity(arg) {
  return arg;
}

接下来:

identity({value: 42}).
                     ^ suggests value: number

3

您可以使用 Jsdoc 传递通用类型,如下所示:

const value = decodeJSON(/** @type {number[]} */("[1,2,3]"))

4
这只是将字符串类型转换为数字数组(这是字符串的错误类型)。它并没有完成问题所要求的操作,它不会给函数提供其类型参数。 - Shayan Toqraee
它并没有做出问题所要求的事情...是的,它已经做到了。请参阅https://github.com/microsoft/TypeScript/issues/27387#issuecomment-659671940。 - milahu

1

我找到了一个可行的解决方案,但它相当丑陋。你的原始解决方案实际上很接近。

基本上,你将通用函数强制转换为更窄版本的自身。下面是代码:

/**
 * @template {unknown} T
 * @param {string} jsonStr
 * @return {T}
 */
function decodeJson(jsonStr) {
    return JSON.parse(jsonStr)
}

// narrow the generic via a type cast
let arr = /** @type {typeof decodeJson<number[]>} */ (decodeJson)("[1, 2, 3]")
console.log(arr) // arr should be typeof "number[]" now

这里是Typescript playground的链接。

虽然上述解决方案有效,但实际上在函数返回后将值进行强制转换可能是一个更好的主意,就像这样:

let arr = /** @type {number[]} */ (decodeJson("[1, 2, 3]"))

非常简洁。


1

考虑到这个问题

const value = decodeJSON<number[]>("[1,2,3]"); // return type will be number[]

我在Github上找到了一些更实用的jsDoc链接

/** @type {ReturnType<typeof decodeJSON<number[]>>} */
const value = decodeJSON("[1,2,3]");

1
这是正确的方式,它还可以用来转换解构元素的类型:const { element } = /** @type {ReturnType<typeof createToaster<{ title: String }>>} */ (createToaster()) - undefined

0
/** 
* @type {string} str
* @returns {number[]} // This infers the type to be returned.
*/
function decodeJSON(str) {
   // ... your logic
};

OP的想法可能是在函数调用时传递返回类型,因为他事先知道解码函数的结果。而在这里,你只是让它成为所有情况下的number[]类型。 - darksoulsong

0
我找到的最令我满意的答案是:
const value = /** @type {decodeJSON<number[]>} */ (decodeJSON)("[1,2,3]"); // Type inference will be unknown

请注意在decodeJSON周围的额外括号。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接