Я в моем остроумии конец с этим. Кажется, это должно работать, я боролся с этим часами, но я не уверен, что не так. Это самый маленький пример, который я смог придумать. У меня даже есть типгард.
/** @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)
Я пытаюсь добавить некоторую безопасность типов в мой ide для javascript, так как я не использую машинописный текст.
Почему тогда вы указали typescript в качестве тега?
Поскольку типы jsdoc обрабатываются сервером typescript-language-server.
Итак, вы используете машинописный текст
Я полагаю, что под «неиспользованием машинописного текста» я имел в виду то, что я не пишу машинописный текст. Но я использую компилятор машинописного текста и не нашел тега typescript-language-server-tag, поэтому пометил его как машинописный.
Я думаю, вы ищете пересечение A и B, а не объединение. Вот исправлен код машинописного текста для вашей проблемы:
interface A {
a: string;
}
interface B {
b: string;
}
type C = A & B;
Я хочу, чтобы TypeC был TypeA или TypeB, а не обоими. Насколько я знаю, пересечения гарантируют наличие свойств каждого типа. Я мог бы делать пересечения, если бы кто-нибудь мог документально подтвердить, что это либо одно, либо другое, а не то и другое.
Я думаю, что мой пример кода в моем вопросе был плохо написан и вводит в заблуждение. Я думаю, что моя реализация подразумевает, что я хочу пересечение. Извини за это. Я отредактировал его, чтобы сделать его более понятным, а также добавил ответ о том, что сработало для меня.
Редактировать: корень этой проблемы заключается в моем непонимании оператора объединения |
и того, как 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/ обновления/…
Когда вы говорите, что значение имеет тип 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
}
Не уверен, какова ваша настройка, но всякий раз, когда у меня возникают проблемы с jsdoc, обычно это происходит из-за того, что комментарии не были обновлены с изменениями. Если вы просто удалите jsdoc и позволите вашей среде IDE сгенерировать их заново, это поможет вам понять, что это должно быть/в чем проблема.