Как ввести защитный тип TypeScript, если instanceof поддерживает только классы?

Нужно ввести 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?

Смотрите Детская площадка. Это отвечает на ваш вопрос? Если нет, то что мне не хватает? (Символы чувствительны к регистру, поэтому ваши тестовые ожидания неверны)

Lesiak 18.11.2022 22:16

Хорошее решение, я смотрел на это и обнаружил, что мы можем проверить список значений, но есть ли способ сделать это без повторения типов объединения?

surajs02 18.11.2022 22:18

Да, проверьте это Детская площадка

Lesiak 18.11.2022 22:22

Вау, я не знал, что мы можем передавать значения в тип, отличное решение. Пожалуйста, опубликуйте это как ответ, и я приму его, чтобы помочь другим :)

surajs02 18.11.2022 22:25

Когда вы создаете класс в TS, вы также автоматически создаете тип с тем же именем, но типы не сохраняются во время выполнения. Ключевое слово typeof также существует как на уровне типа, так и во время выполнения, как вы обнаружили, но оно не делает то же самое в обоих контекстах. Это скорее исключения, чем правило. Когда вы используете typeof на уровне типа, вы можете вывести только тот тип значений, который может быть известен статически.

geoffrey 18.11.2022 22:36

Я не тестировал typeof во время компиляции и во время выполнения, так как возникла путаница вокруг instanceof и использования с type, но хорошее предложение и хорошая мысль о type, созданном с помощью класса - я не понял, что произошло

surajs02 18.11.2022 22:52

Да, я немного предвидел. Я так понимаю, вы пришли из языка, отличного от JS, и могут возникнуть путаницы и разочарования в отношении того, что такое уровень типов и что такое время выполнения. Некоторые варианты, такие как ключевое слово typeof, немного сомнительны.

geoffrey 18.11.2022 22:57

Да, все еще изучаю пространство типов TS, путаница обычно связана с использованием типа в пространстве значений, любые ресурсы по этому и рекурсии типов будут оценены

surajs02 18.11.2022 23:00
Шаблоны Angular PrimeNg
Шаблоны Angular PrimeNg
Как привнести проверку типов в наши шаблоны Angular, использующие компоненты библиотеки PrimeNg, и настроить их отображение с помощью встроенной...
Освоение принципов SOLID в JavaScript: Пошаговое руководство
Освоение принципов SOLID в JavaScript: Пошаговое руководство
Принцип единой ответственности (SRP): класс должен иметь только одну причину для изменения. Другими словами, у него должна быть только одна...
В чем разница между Promise и Observable?
В чем разница между Promise и Observable?
Разберитесь в этом вопросе, и вы значительно повысите уровень своей компетенции.
Создание собственной системы электронной коммерции на базе Keystone.js - настройка среды и базовые модели
Создание собственной системы электронной коммерции на базе Keystone.js - настройка среды и базовые модели
Прошлая статья была первой из цикла статей о создании системы электронной коммерции с использованием Keystone.js, и она была посвящена главным образом...
0
8
56
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Обнаружено возможное, но не идеальное решение для защиты типа строк объединения, чтобы проверить список строк объединения:

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 работает с классами. Хорошее решение типа массива, чтобы избежать повторяющихся строк объединения. Спасибо за объяснение :)

surajs02 18.11.2022 22:52

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:

  1. контекст выражения (части, которые будут выпущены позже как JavaScript)
  2. контекст типа (части 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 18.11.2022 22:41

Ха-ха. Да, я вижу, что вы говорите.

Orwa Diraneyya 18.11.2022 22:45

Спасибо за ваш отзыв, @geoffrey, я попытался перефразировать, чтобы сделать мое сообщение более понятным.

Orwa Diraneyya 18.11.2022 22:53

Хорошее замечание о понятиях TypeScript, не существующих во время выполнения, я знал об этом, но пытался найти способ утвердить тип во время выполнения, что, как я понимаю, возможно только со значениями. Я не использовал c: Letter: c is Letter из-за утверждения неизвестных значений, как упомянул @geoffrey

surajs02 18.11.2022 22:57

Да, я улучшил свой ответ и добавил к нему, чтобы отметить гениальное использование typeof @Lesiak

Orwa Diraneyya 18.11.2022 23:08

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