In a .ts file you can write first<string>(rows). In a .js file you cannot: first<string>(rows) is read as two comparisons, first < string and > (rows), and tsc reports operator errors (TS2365 and TS2693), as the second example shows. Every type argument in JavaScript is inferred.
TypeScript infers from the arguments first, and from the type the result is assigned to when the arguments say nothing. When neither helps, a type parameter in a .js file becomes any, which turns checking off for that value.
At a glance
| Use | JSDoc in a .js file | TypeScript in a .ts file |
|---|---|---|
| Set by the variable's type | /** @type {{ items: number[] }} */
const counts = createStore(); | const counts = createStore<number>(); |
| Keep literal types | /** @template const T */ | <const T> |
Examples
/**
* @template T
* @returns {{ items: T[] }}
*/
export function createStore() {
return { items: [] };
}
// T has no inference source here, so in a .js file it becomes any: nothing is checked.
const loose = createStore();
loose.items.push(1, "two");
/** @type {{ items: number[] }} */
const counts = createStore();
counts.items.push(1);
counts.items.push("two");error TS2345: Argument of type 'string' is not assignable to parameter of type 'number'.
export { loose, counts };jsconfig.json for this example
{
"compilerOptions": {
"checkJs": true,
"noEmit": true,
"strict": true,
"target": "es2022",
"module": "nodenext",
"types": []
},
"include": ["*.js"]
} /**
* @template T
* @param {T[]} items
* @returns {T | undefined}
*/
export function first(items) {
return items[0];
}
/** @type {unknown[]} */
const rows = ["TEE-01"];
const sku = first<string>(rows);error TS2365: Operator '>' cannot be applied to types 'boolean' and 'unknown[]'.error TS2693: 'string' only refers to a type, but is being used as a value here.
export { sku };jsconfig.json for this example
{
"compilerOptions": {
"checkJs": true,
"noEmit": true,
"strict": true,
"target": "es2022",
"module": "nodenext",
"types": []
},
"include": ["*.js"]
} /**
* @template const T
* @param {T} routes
* @returns {T}
*/
export function defineRoutes(routes) {
return routes;
}
const routes = defineRoutes({ home: "/", cart: "/cart" });
/** @type {"/cart"} */
const cartPath = routes.cart;
export { cartPath };export function defineRoutes<const T>(routes: T): T {
return routes;
}
const routes = defineRoutes({ home: "/", cart: "/cart" });
const cartPath: "/cart" = routes.cart;
export { cartPath };jsconfig.json for this example
{
"compilerOptions": {
"checkJs": true,
"noEmit": true,
"strict": true,
"target": "es2022",
"module": "nodenext",
"types": []
},
"include": ["*.js"]
} Steering inference without type arguments
Type the variable that receives the result: /** @type {{ items: number[] }} */ const counts = createStore(); makes T a number, as the first example shows, where the untyped loose gets any.
Or type an argument: a typed empty array, /** @type {string[]} */ ([]), passed to the function gives T its type. A cast on the call's result also works, but it only claims the type rather than checking it.
const type parameters
TypeScript 5.0 added const type parameters, which infer the narrowest literal types, as if the caller had written as const. In JSDoc the modifier goes before the name, @template const T, and with TypeScript 5.9 it works as in the third example: routes.cart is typed "/cart", not string.
Gotchas
- The any fallback is specific to JavaScript files; in a
.tsfile the same call infersunknown. - A type parameter used only in the return type, as in
createStore, can never be inferred from arguments. Consider an optional parameter that mentions it, or a default.
Questions
How do I pass a type argument to a generic function in JavaScript?
You cannot write one. Give the variable that receives the result a type with @type, type one of the arguments, or give the type parameter a default.
Sources
- Generics: working with generic type variables · TypeScript handbook
- TypeScript 5.0: const type parameters · TypeScript release notes
- Type Checking JavaScript Files: unspecified type parameters default to any · TypeScript handbook
Last reviewed 20 October 2026.