Как написать универсальный тип защиты в TypeScript?

Нерабочая защита типа

Я пытаюсь написать универсальную защиту типа для разных универсальных типов Token, но она не работает. Мой текущий подход выглядит так:

export function isToken<T extends Token>(token: any): token is T {
  for (const key in keyof T) {  // (1) for every key in the given token type T
    if (  // check if one of the two criteria are not
        !(key in token) ||  // the key is not in the token or
        typeof token[key] !== typeof T[key]  // (2) the type for that key-value-pair is not the
                                             // same as in the given token type T
    ) {
      return false;
    }
  }
  
  return true;
}

Но TSC жалуется, что:

  • в строке (1): Cannot find name 'keyof'.ts(2304) - значит ключевое слово keyof не работает
  • в очереди (2): 'T' only refers to a type, but is being used as a value here.ts(2693)

Как я могу исправить свою защиту типа?

Жетоны

Токены — это просто типы для описания различных полезных нагрузок JWT:

const type Token = {
  userId: string;
}

const type LoginToken extends Token = {
  role: 'User' | 'Admin'
}

const type EmailVerificationToken extends Token = {
  email: string;
}

Защита текущего типа

Мне удалось написать общую функцию проверки токена, но у меня все еще есть дополнительная защита типа для типа токена:

// for each token type I have a function like below
// this is what I'm trying to handle with a single generic type guard
export function isVerificationToken(token: any): token is VerificationToken {
  return typeof token.userId === 'string' && typeof token.email === 'string';
}

export function verifyVerificationToken(token: string) {
  return verifyToken<VerificationToken>(token, isVerificationToken);
}

function verifyToken<T>(token: string, tokenTypeCheck: (token: any) => token is T) {
  const decoded = jwt.verify(token, config.jwtSecret);
  if (tokenTypeCheck(decoded)) {
    return decoded;
  }
  return null;
}
const type Token extends Object = { не является TypeScript. Что это? Предоставьте минимальный воспроизводимый пример, который четко демонстрирует проблему, с которой вы столкнулись. В идеале кто-то может вставить код в автономную IDE, такую ​​как Площадка TypeScript (ссылка здесь!), и сразу же приступить к решению проблемы, не создавая ее заново. Таким образом, не должно быть псевдокода, опечаток, несвязанных ошибок или необъявленных типов или значений.
jcalz 22.04.2022 21:33

Типы TypeScript не существуют во время выполнения; они стертый. Похоже, что многие из этих вопросов объединяют статическую систему типов TypeScript с кодом JavaScript во время выполнения. Когда я читаю const key in keyof T, я не понимаю, что вы пытаетесь сделать. Подумайте, как бы вы написали все это на чистом JavaScript, а затем мы можем представить добавление к нему типов.

jcalz 22.04.2022 21:38
В чем разница между Promise и Observable?
В чем разница между Promise и Observable?
Разберитесь в этом вопросе, и вы значительно повысите уровень своей компетенции.
Создание собственной системы электронной коммерции на базе Keystone.js - настройка среды и базовые модели
Создание собственной системы электронной коммерции на базе Keystone.js - настройка среды и базовые модели
Прошлая статья была первой из цикла статей о создании системы электронной коммерции с использованием Keystone.js, и она была посвящена главным образом...
0
2
39
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Проблема здесь в том, что Typescript больше не существует во время выполнения. Он переносится в JavaScript, и поэтому вы не можете выполнять оценку типов во время выполнения (как вы можете делать в C# с Reflection или подобным), поскольку в JavaScript нет никаких типов. Таким образом, передача T в качестве универсального параметра функции, а затем использование его нестатическим способом (для динамического сравнения типов свойств и т. д.) невозможна. Чтобы это работало, вам нужно предоставить объект этого типа функции, а затем использовать его для оценки типов членов.

Другая проблема заключается в том, что вы используете keyof для получения списка всех ключей в объекте, хотя это не то, что делает ключевое слово keyof. см.: документы ТС. Как мы установили: во время выполнения нет типов, поэтому keyof будет работать только в статическом контексте.

Рабочее решение вашей проблемы (хотя, очевидно, не такое элегантное, как вы могли бы ожидать, глядя на концепции, присутствующие в других языках, таких как C# и т. д.), было бы передать объект типа T вашей функции, а вместо используя keyof, вы бы использовали Object.keys, чтобы получить все ключи объекта.

export function isToken<T extends Token>(expected: T, token: any): token is T {
  for (const key in Object.keys(expected)) {
    if (!(key in token) || typeof token[key] !== typeof (expected as any)[key]) {
      return false;
    }
  }
  
  return true;
}

// Or a bit shorter:

export function isToken<T extends Token>(expected: T, token: any): token is T { 
  return Object
    .keys(expected)
    .every((key) => key in token && typeof token[key] === typeof expected[key]);
}

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

Похожие вопросы

Передача vscode во внешние функции
Firebase: Почему мой код использует только первый документ в моей коллекции?
Реагировать на ошибку машинописного текста - элемент неявно имеет любой тип, потому что выражение строки типа не может использоваться для индексирования типа {}
Как получить доступ к вложенным необязательным индексам из интерфейса
Почему машинописный текст не может контекстуально вывести эти типы промежуточного программного обеспечения
Передать конструктор класса как функцию в другом классе
React & clsx: добавьте имя класса, если текущий элемент в сопоставленном массиве является первым из нескольких элементов
Свойство «MathFun» отсутствует в типе «(x?: число, y?: число) => число», но требуется в типе «Func».
Получение строки типа не может быть назначено строке типа для компонента TS в сборнике рассказов
Как ввести useState для файлов?