Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JsDoc union type with objects not working

I'm at my wits end with this. It seems like it should work, I've been wrestling with it for hours, but I'm not sure what's wrong. This is the smallest example I could come up with. I even have a type guard.

/** @typedef {{ a: string }} TypeA*/
/** @typedef {{ b: string }} TypeB*/
/** @typedef {(TypeA | TypeB) } TypeC */

/** @type {TypeC} */
let typeC;

console.log(typeC.b) // autocompletion for property b doesn't work

I get the error:

Property 'b' does not exist on type '{ a: string; } | { b: string; }'.
  Property 'b' does not exist on type '{ a: string; }'.ts(2339)
like image 483
Dylan Landry Avatar asked Dec 14 '20 18:12

Dylan Landry


People also ask

How do you handle a union type in TypeScript?

TypeScript Union Type Narrowing To narrow a variable to a specific type, implement a type guard. Use the typeof operator with the variable name and compare it with the type you expect for the variable.

Can you use JSDoc with TypeScript?

You can use most JSDoc type syntax and any TypeScript syntax, from the most basic like string to the most advanced, like conditional types.

Does not exist on type Union?

The "property does not exist on type union" error occurs when we try to access a property that is not present on every object in the union type. To solve the error, use a type guard to ensure the property exists on the object before accessing it.

When would you use an intersection type instead of a union type?

Intersection types are closely related to union types, but they are used very differently. An intersection type combines multiple types into one. This allows you to add together existing types to get a single type that has all the features you need.


Video Answer


2 Answers

I found that a jsdoc style type guard lets me access the properties of TypeC if I am able to duck type it as TypeB.

/** @typedef {{ a: string }} TypeA*/
/** @typedef {{ b: string }} TypeB*/
/** @typedef {(TypeA | TypeB) } TypeC */

/**
 * @param {*} value
 * @returns {value is TypeB}
 */
function typeIsB(value) {
  return true;
}

/** @type {TypeC} */
let typeC;

if (typeIsB(typeC)) {
  console.log(typeC.b) // no error, autocomplete works when typing typeC.b
}

Screenshot of autocomplete working:

enter image description here

like image 182
Dylan Landry Avatar answered Oct 10 '22 01:10

Dylan Landry


I think you are looking for the intersection of A and B, not the union. Here is the typescript code for your issue fixed:

interface A {
  a: string;
}

interface B {
  b: string;
}

type C = A & B;
like image 40
Passersby Avatar answered Oct 10 '22 03:10

Passersby