Home > Enterprise >  JSDOC/TypeScript - How do I read a generic object parameter as strongly typed?
JSDOC/TypeScript - How do I read a generic object parameter as strongly typed?

Time:01-20

I want to make it so a generic parameter (this generic parameter would be used as a function argument) in a function implicitly resolves to its literal type of the argument passed in. So far, I've determined that this only works when you explicitly define the type before passing it.

I'm not sure if I wording it right when I say "strongly typed", but here is the rundown of my question.

/**
 * @template T
 * @property {T} o
 */
function myFunction(o) {
    // ...doStuff
}

When I call the function, I want to get something like this:

what I want

I get that when I call the function like so:

/** @type {{x: 1, y: 2, z: 3}} */
const o = {
    x: 1,
    y: 2,
    z: 3
};
myFunction(o);

But, I don't want to have to do this every time, instead I want to call the function like this:

myFunction({
    x: 1,
    y: 2,
    z: 3
});

The problem is this is how it becomes typed:

what I have

I appreciate any and all help. I hope this is possible, I've done a lot of things in TypeScript/JSDOC but this one is stumping me. Maybe it is because I am tired.

CodePudding user response:

The difference I see in the screenshots is that you don't want the types to be widened in the usual way; you want the type of the object created by {x: 1} (for instance) to be {x: 1}, not {x: number}. But normally, the type of the object created by {x: 1} will be widened to {x: number}.

In TypeScript itself, you could use as const on the object literal to prevent that widening ({x: 1} as const).

With JSDoc, according to this issue in the TypeScript issues list, you can use a @type {const} documentation comment:

myFunction(/** @type {const} */{
    x: 1,
    y: 2,
    z: 3
});

or possibly (code examples in the issue seem to use ()):

myFunction(/** @type {const} */({
    x: 1,
    y: 2,
    z: 3
}));

I'm not sure what you're doing to get those screenshots, so I can't verify it, but as const would be how you do this in TypeScript itself, and the issue is talking about doing that in JSDoc, so I'd expect that to work.

CodePudding user response:

After a nice night of sleep and some rest, I was able to do all the searching I needed in order to find the correct answer:

Thanks T.J.'s answer, I was able to use the right terminology that led me to find the library "ts-toolbelts".

Terminology being in this context that I wanted to prevent my inferred type from being widened.

/**
 * Used in "Narrow" to recursively dig into arrays/objects and narrow them down to their literal type.
 * @template T Type to narrow down
 * @typedef {(T extends [] ? [] : never)|(Try<T, (string|number|bigint|boolean)>)|({[K in keyof T]: Try<T[K], Function, NarrowRaw<T[K]>>})} NarrowRaw
 */

/**
 * Explicitly checks to see if TTypeToCheck is of type TTypeToForce. If it is, then it returns TTypeToCheck, otherwise it returns Catch.
 * @template TTypeToCheck Type to check for
 * @template TTypeToForce Type to check against
 * @template {any} [Catch=never] Type to return if the type check fails
 * @typedef {TTypeToCheck extends TTypeToForce ? TTypeToCheck : Catch} Try
 */

/**
 * Prevents widening on an generic parameter.
 * @template T Type to narrow
 * @typedef {Try<T, [], NarrowRaw<T>>} Narrow
 */

myFunction({
    x: 1,
    y: 2,
    z: 3
}); // now infers the type of: "{ x: 1, y: 2, z: 3 }" instead of "{ x: number, y: number, z: number}".
  • Related