Нужно ввести guard, но instanceof не работает с TypeScript types:
type Letter = 'A' | 'B';
const isLetter = (c: any): c is Letter => c instanceof Letter; // Error: 'Letter' only refers to a type, but is being used as a value here.
// Expected usage: Filter via type guard.
isLetter('a'); // Should output true
'foo bar'.split('').filter(c => isLetter(c)); // Should output 'a'
Не нашел похожих вопросов, но instanceof работает с классами:
class Car {}
const isCar = (c: any): c is Car => c instanceof Car; // No error
isCar('a'); // false
Если instanceof работает только с классами, что эквивалентно type, и как мы можем ввести охрану с помощью TypeScript type?
Хорошее решение, я смотрел на это и обнаружил, что мы можем проверить список значений, но есть ли способ сделать это без повторения типов объединения?
Да, проверьте это Детская площадка
Вау, я не знал, что мы можем передавать значения в тип, отличное решение. Пожалуйста, опубликуйте это как ответ, и я приму его, чтобы помочь другим :)
Когда вы создаете класс в TS, вы также автоматически создаете тип с тем же именем, но типы не сохраняются во время выполнения. Ключевое слово typeof также существует как на уровне типа, так и во время выполнения, как вы обнаружили, но оно не делает то же самое в обоих контекстах. Это скорее исключения, чем правило. Когда вы используете typeof на уровне типа, вы можете вывести только тот тип значений, который может быть известен статически.
Я не тестировал typeof во время компиляции и во время выполнения, так как возникла путаница вокруг instanceof и использования с type, но хорошее предложение и хорошая мысль о type, созданном с помощью класса - я не понял, что произошло
Да, я немного предвидел. Я так понимаю, вы пришли из языка, отличного от JS, и могут возникнуть путаницы и разочарования в отношении того, что такое уровень типов и что такое время выполнения. Некоторые варианты, такие как ключевое слово typeof, немного сомнительны.
Да, все еще изучаю пространство типов TS, путаница обычно связана с использованием типа в пространстве значений, любые ресурсы по этому и рекурсии типов будут оценены
Обнаружено возможное, но не идеальное решение для защиты типа строк объединения, чтобы проверить список строк объединения:
const isLetter = (c: any): c is Letter => ['A', 'B'].includes(c);
isLetter('A'); // true
isLetter('a'); // false
Не идеально, поскольку строки объединения дублируются в типе и защите типа - открыты для лучших решений.
Типы TS существуют только на этапе компиляции, а не во время выполнения.
В пользовательской защите типов вы обязаны реализовать соответствующие проверки. Проверка будет варьироваться в зависимости от типа ввода и вывода — выбор между несколькими альтернативами проще, чем утверждение формы совершенно неизвестного объекта.
Для вашего типа Letter (союз «A» и «B») достаточно проверить, является ли ввод A или ввод B
const isLetter = (c: any): c is Letter => c == 'A' || c == 'B';
Если в вашем союзе больше членов и вы не хотите повторять их в союзе и в type guard:
const letters = ['A', 'B'] as const;
type Letter = typeof letters[number];
const isLetter = (c: any): c is Letter => letters.includes(c);
Ссылка на игровую площадку
Примечание. Классы сохраняются во время выполнения (через цепочку прототипов), поэтому вы можете использовать оператор instanceof с классами.
Объяснение классов, существующих во время выполнения, было полезно для разъяснения того, почему instanceof работает с классами. Хорошее решение типа массива, чтобы избежать повторяющихся строк объединения. Спасибо за объяснение :)
Typescript — это технология времени компиляции. Вы пытаетесь использовать тип Typescript, концепция которого существует только во время компиляции, для создания проверки времени выполнения (проверки, которая должна выполняться во время выполнения JavaScript).
К тому времени, когда сгенерированный JavaScript будет выполнен, понятия TypeScript уже давно исчезнут (то есть они уже убраны).
Следовательно, вы не должны пытаться реализовать эту проверку в теле функции как проверку во время выполнения, используя интерфейс/тип TypeScript. Так не пойдет.
С другой стороны, чтобы реализовать проверку во время компиляции, достаточно просто заменить any для типа параметра типом TypeScript Letter.
Этот код может помочь прояснить этот вопрос:
type Letter = 'A' | 'B';
// compile-time check is implemented by assigning the parameter 'c'
// to a TypeScript type
const shouldBeLetter = (c: Letter) => {
// implement a runtime check using JavaScript, not TypeScript
if (c !== 'A' && c !== 'B') throw "c should be either 'A' or 'B'";
/* rest of the function */
}
// example of a statament that fails at compile time
// error: Argument of type '"D"' is not assignable to parameter of
// type 'Letter'. (2345)
shouldBeLetter('C');
// example of a statement that won't fail at compile time, but will
// need a runtime test
let couldBeC : Letter = 'ABC'[Math.floor(Math.random() * 3)] as Letter;
shouldBeLetter(couldBeC);
PS: Предоставление типов времени компиляции в TypeScript, которые удаляются из выпущенных (скомпилированных) файлов JavaScript, создает два разных контекста в файле кода TypeScript:
В так называемом контексте типа можно переназначить некоторые ключевые слова JavaScript, чтобы они имели другое значение в TypeScript. Примером может служить ключевое слово typeof: https://www.typescriptlang.org/docs/handbook/2/typeof-types.html
В JavaScript typeof — это унарный оператор, который возвращает строку во время выполнения, эта строка соотносится с типом следующего за ней значения/переменной.
... при использовании в TypeScript в контексте типа он дает эквивалентный TypeScript тип значения/переменной, следующего за ним.
Точно так же понятия, подобные выражениям, такие как letters[number], могут использоваться в контексте типа в TypeScript для получения информации о типе из фактических переменных, как описано здесь: https://www.typescriptlang.org/docs/handbook/2. /indexed-access-types.html
Обе точки очень аккуратно используются в другом ответе @Lesiak при повторном использовании массива перечисляемого типа между контекстом выражения и контекстом типа в коде.
(c: Letter): c is Letter
не имеет большого смысла. Если вы уже знаете, что это Letter
, вам не нужна защита типа. У меня такое же горе по поводу другого вашего примера. Возможно, перефразируйте то, что вы пытались сказать.
Ха-ха. Да, я вижу, что вы говорите.
Спасибо за ваш отзыв, @geoffrey, я попытался перефразировать, чтобы сделать мое сообщение более понятным.
Хорошее замечание о понятиях TypeScript, не существующих во время выполнения, я знал об этом, но пытался найти способ утвердить тип во время выполнения, что, как я понимаю, возможно только со значениями. Я не использовал c: Letter: c is Letter из-за утверждения неизвестных значений, как упомянул @geoffrey
Да, я улучшил свой ответ и добавил к нему, чтобы отметить гениальное использование typeof @Lesiak
Смотрите Детская площадка. Это отвечает на ваш вопрос? Если нет, то что мне не хватает? (Символы чувствительны к регистру, поэтому ваши тестовые ожидания неверны)