Typescript: Allow to explicitly pass parameter types via JSDoc

Created on 27 Sep 2018  路  5Comments  路  Source: microsoft/TypeScript

Search Terms

jsdoc generics type parameters constraints

Suggestion

It seems like it is not possible via JSDoc to explicitly tell compiler which type parameters to pass to a generic function.

Use Cases

In TypeScript it is possible to explicitly pass type parameters such as mongoose.model<Model, Schema>('modelName', Schema) while I could not find a way to do same with JSDoc.

Examples

mongoose.model has two signatures, first one with one type parameter and the other with two. To make use of the second signature we must pass types explicitly.

export function model<T extends Document>(
  name: string,
  schema?: Schema,
  collection?: string,
  skipInit?: boolean
): Model<T>;

export function model<T extends Document, U extends Model<T>>(
  name: string,
  schema?: Schema,
  collection?: string,
  skipInit?: boolean
): U;
//  Choose second signature in typescript.
const model = mongoose.model<Model, Schema>('modelName', Schema);

// With JSDoc, compiler always chooses first signature and we receive a type mismatch error.
/** @type {Schema & mongoose.Model<Model>} */
const model = mongoose.model('modelName', Schema);

// But something like this would be great.
const model = mongoose.model/**<Model, Schema>*/('modelName', Schema);

My apologies if this is already possible, but I've spend almost a week battling this.

Related: https://github.com/Microsoft/TypeScript-Node-Starter/issues/101

Checklist

My suggestion meets these guidelines:

  • [x] This wouldn't be a breaking change in existing TypeScript / JavaScript code
  • [x] This wouldn't change the runtime behavior of existing JavaScript code
  • [x] This could be implemented without emitting different JS based on the types of the expressions
  • [ ] This isn't a runtime feature (e.g. new expression-level syntax)
JSDoc JavaScript In Discussion Suggestion checkJs

Most helpful comment

Another use case with React Hooks:

// useState<string> is derived correctly
const [aString, setAString] = useState("default value");

Removing default value there is no way to pass type info:

// useState<any>
const [aString, setAString] = useState();

All 5 comments

I would also like to see something like this. Currently it's really hard to use functions with generic types from JS.

Another use case with React Hooks:

// useState<string> is derived correctly
const [aString, setAString] = useState("default value");

Removing default value there is no way to pass type info:

// useState<any>
const [aString, setAString] = useState();

Anything on this? Also stuck on the useState example as shared above:

// Doesn't set type properly
const [error, setError] = /** @type {React.useState<?string>} */ useState(null);
// Nor does this
/** @type {[?string, React.Dispatch<React.SetStateAction<?string>>]} */
const [error, setError] = /** @type {React.useState<?string>} */ useState(null);


// we have an error we want to set now.
setError('Error!') // Shows a type error

Edit: Ok this might be a hack, but in the case of the useState example it works:

const [error, setError] = useState(/** @type {?string} */ (null));

implied types for error are string | null and setError is React.Dispatch<React.SetStateAction<?string>>

i think what's happening "under the hood" is we are forcing the "type" that useState is being passed.

For functions that take an argument of the generic type, casting the argument itself is reasonably practical. We've settled on these patterns with useState and useRef in React for example:

const [thing, setThing] = useState(/** @type {SomeType|null} */ (null));
const widgetRef = useRef(/** @type {HTMLElement|null} */(null));

In cases where there is no argument for everything else to be inferred from, it gets much more verbose unfortunately. What would be helpful is being able to do something like:

const aSet = /** @type {?<string>} */(new Set());

Where the ? is inferred as Set. In this example it only saves a few characters, but it could be a lot more if the generic type name is lengthy and/or has to be imported from somewhere.

Where the ? is inferred as Set. In this example it only saves a few characters, but it could be a lot more if the generic type name is lengthy and/or has to be imported from somewhere.

Something like that would be awesome, it would majorly simplify the code required for React.forwardRef and React.memo:

```diff
- const Component = /* @type {React.ForwardRefExoticComponent>} */ (React.forwardRef(() => { ... }))
+ const Component = /
* @type {?} */ (React.forwardRef(() => { ... }))

Was this page helpful?
0 / 5 - 0 ratings

Related issues

kyasbal-1994 picture kyasbal-1994  路  3Comments

DanielRosenwasser picture DanielRosenwasser  路  3Comments

Zlatkovsky picture Zlatkovsky  路  3Comments

manekinekko picture manekinekko  路  3Comments

bgrieder picture bgrieder  路  3Comments