Тип объединения JsDoc с неработающими объектами

Я в моем остроумии конец с этим. Кажется, это должно работать, я боролся с этим часами, но я не уверен, что не так. Это самый маленький пример, который я смог придумать. У меня даже есть типгард.

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

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

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

Я получаю сообщение об ошибке:

Property 'b' does not exist on type '{ a: string; } | { b: string; }'.
  Property 'b' does not exist on type '{ a: string; }'.ts(2339)

Не уверен, какова ваша настройка, но всякий раз, когда у меня возникают проблемы с jsdoc, обычно это происходит из-за того, что комментарии не были обновлены с изменениями. Если вы просто удалите jsdoc и позволите вашей среде IDE сгенерировать их заново, это поможет вам понять, что это должно быть/в чем проблема.

mwilson 14.12.2020 20:02

Я пытаюсь добавить некоторую безопасность типов в мой ide для javascript, так как я не использую машинописный текст.

Dylan Landry 15.12.2020 16:08

Почему тогда вы указали typescript в качестве тега?

mwilson 15.12.2020 17:53

Поскольку типы jsdoc обрабатываются сервером typescript-language-server.

Dylan Landry 16.12.2020 18:39

Итак, вы используете машинописный текст

mwilson 16.12.2020 20:27

Я полагаю, что под «неиспользованием машинописного текста» я имел в виду то, что я не пишу машинописный текст. Но я использую компилятор машинописного текста и не нашел тега typescript-language-server-tag, поэтому пометил его как машинописный.

Dylan Landry 18.12.2020 22:22
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
7
6
4 230
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Я думаю, вы ищете пересечение A и B, а не объединение. Вот исправлен код машинописного текста для вашей проблемы:

interface A {
  a: string;
}

interface B {
  b: string;
}

type C = A & B;

Я хочу, чтобы TypeC был TypeA или TypeB, а не обоими. Насколько я знаю, пересечения гарантируют наличие свойств каждого типа. Я мог бы делать пересечения, если бы кто-нибудь мог документально подтвердить, что это либо одно, либо другое, а не то и другое.

Dylan Landry 15.12.2020 16:07

Я думаю, что мой пример кода в моем вопросе был плохо написан и вводит в заблуждение. Я думаю, что моя реализация подразумевает, что я хочу пересечение. Извини за это. Я отредактировал его, чтобы сделать его более понятным, а также добавил ответ о том, что сработало для меня.

Dylan Landry 15.12.2020 16:14
Ответ принят как подходящий

Редактировать: корень этой проблемы заключается в моем непонимании оператора объединения | и того, как TypeScript сужает типы. Tsserver, языковая служба машинописного текста, интерпретировала эти комментарии JSDoc и предоставляла такие языковые службы, как автозаполнение. Я напишу немного TypeScript, чтобы правильно объяснить, как типы работают в этой ситуации.

Учитывая эту ситуацию:

type TypeA = { a: string };
type TypeB = { b: string };
type TypeC = TypeA | TypeB;

const typeC: TypeC = { /* omitted */ };

typeC может быть либо TypeA, либо TypeB, но мы не знаем, какой именно. Итак, TypeScript не позволит нам получить доступ к typeC.a или typeC.b, потому что мы не написали никакого кода, который сужает typeC до TypeA или TypeB. Вот почему автозаполнение не работает. И именно поэтому в моем предыдущем ответе ниже работает утиная печать или использование защиты типа, которая утверждает, что переменная равна TypeB.

Таким образом, с объединением вам нужно сузить этот тип во время выполнения, чтобы иметь возможность безопасного доступа к свойствам. Возможно, проверив, присутствует ли набор свойств, уникальных для одного из возможных типов, или утверждение с использованием as, или что-то еще.

Другой подход — вместо этого использовать перекресток . Пересечение говорит, что тип имеет все свойства обоих типов. Это будет работать:

type TypeC = TypeA & TypeB;

Затем мы можем получить доступ к typeC.a и typeC.b.

Ниже мой предыдущий ответ, сохраненный по историческим причинам.


Я обнаружил, что защита типа в стиле jsdoc позволяет мне получить доступ к свойствам TypeC, если я могу уклониться от ввода его как 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.info(typeC.b) // no error, autocomplete works when typing typeC.b
}

Скриншот работы автозаполнения:

Для тех, кто натыкается на это, это почти наверняка не стандартная функция JSDoc, а часть Typescript, в частности, это предикат типа, см. typescriptlang.org/docs/handbook/2/… и code.visualstudio.com/ обновления/…

ksrb 03.01.2023 19:45

Когда вы говорите, что значение имеет тип A или B, компилятор машинописного текста не знает, к какому типу значения относится эта переменная.

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

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

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

Вам нужно условие для определения фактического типа значения, на которое ссылается typeC. Например:

if ('a' in typeC) {
    console.info(typeC.a) // autocompletion for property a works       
} else {
    console.info(typeC.b) // autocompletion for property b works       
}

Другие вопросы по теме