Есть ли способ получить как полезное, так и типовое знание из предиката типа в TypeScript?

При проверке данных иногда требуется: - действительны ли данные - если нет, ошибки проверки

Можем ли мы сделать это естественным, JS-способом в TypeScript? Работает ли что-то вроде следующего?


type Person = {
    name: string,
    height: number,
};

function validatePerson(obj: object): obj is Person & string[] {

    const missingKeys = ["dependencies", "name"]
        .filter(key => key in obj)
        .map(key => `key ${key} is missing from obj`);

    if (!missingKeys.length) {
        return missingKeys as typeof missingKeys & true;
    }
    else {
        return missingKeys as typeof missingKeys & false;
    }
}

const x = { name: "ff", height: getHeight()};
const validationErrors = validatePerson(x);
if (!validationErrors.length) {
    const b: Person = x;
}

function getHeight(): any {
    return 40;
}

Лучшее, что я могу заставить на самом деле работать, это эта странная штука в стиле передачи указателя, но мне просто недостаточно JS-y, чтобы хотеть это на master:

type Person = {
    name: string,
    height: number,
};

function validatePerson(obj: object, placeToPushValidationErrors: string[]): obj is Person {

    const missingKeys = ["dependencies", "name"]
        .filter(key => key in obj)
        .map(key => `key ${key} is missing from obj`);

    placeToPushValidationErrors.push(...missingKeys);

    return !missingKeys.length;
}

// imagine this is coming from untrustworthy JSON
function getHeight(): unknown {
    return 40;
}

const person = { name: "ff", height: getHeight() };
const validationErrors = [];
if (validatePerson(person, validationErrors)) {
    const p: Person = person;
}
else {
    throw Error(`invalid person: ${validationErrors.join(', ')}`)
}

Нет, пользовательская защита типа должна возвращать param is type. Вы можете вернуть Person | string[], поместить его в переменную и использовать if ( usa_bbox instanceof Array) .

Titian Cernicova-Dragomir 06.02.2019 17:31

К сожалению, определяемые пользователем типы защиты типа в форме x is Y на самом деле не являются первоклассными гражданами и не могут перемещаться или компоноваться так, как это могут делать другие типы. Таким образом, вы не можете вернуть что-то вроде {isValid: obj is Person, validationErrors: string[]}. Вы можете отказаться от защиты типов и вернуть что-то вроде {person?: Person, validationErrors: string[]}, а затем использовать, определено ли свойство person как защита.

jcalz 06.02.2019 17:51

@jcalz, если вы сделаете свой комментарий ответом, я отмечу его правильным. Это действительно интересное предложение.

Max Heiber 07.02.2019 16:15
Зод: сила проверки и преобразования данных
Зод: сила проверки и преобразования данных
Сегодня я хочу познакомить вас с библиотекой Zod и раскрыть некоторые ее особенности, например, возможности валидации и трансформации данных, а также...
Как заставить Remix работать с Mantine и Cloudflare Pages/Workers
Как заставить Remix работать с Mantine и Cloudflare Pages/Workers
Мне нравится библиотека Mantine Component , но заставить ее работать без проблем с Remix бывает непросто.
Угловой продивер
Угловой продивер
Оригинал этой статьи на турецком языке. ChatGPT используется только для перевода на английский язык.
TypeScript против JavaScript
TypeScript против JavaScript
TypeScript vs JavaScript - в чем различия и какой из них выбрать?
Синхронизация localStorage в масштабах всего приложения с помощью пользовательского реактивного хука useLocalStorage
Синхронизация localStorage в масштабах всего приложения с помощью пользовательского реактивного хука useLocalStorage
Не все нужно хранить на стороне сервера. Иногда все, что вам нужно, это постоянное хранилище на стороне клиента для хранения уникальных для клиента...
Что такое ленивая загрузка в Angular и как ее применять
Что такое ленивая загрузка в Angular и как ее применять
Ленивая загрузка - это техника, используемая в Angular для повышения производительности приложения путем загрузки модулей только тогда, когда они...
0
3
21
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

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

К сожалению, для этого нельзя использовать предикаты типов. Их нельзя использовать в объединениях, пересечениях или в качестве типов свойств объекта. Единственное место, где они действительно могут появиться, — это тип возвращаемого значения для функции, и они привязаны ровно к одному параметру, переданному в функцию. И предикаты типа связаны с boolean таким образом, который вам здесь не нужен, поскольку вы хотите, чтобы тип возвращаемого значения был массивом, но true и false не являются массивами.

В любом случае, я бы предложил сделать здесь что-то другое... вернуть объект, содержащий как нужный массив, так и по выбору a Person. Если Person присутствует, проверка прошла успешно; в противном случае используйте массив ошибок проверки:

function validatePerson(obj: object) {

  const validationErrors = ["dependencies", "name"]
    .filter(key => key in obj)
    .map(key => `key ${key} is missing from obj`);

  const ret: { person?: Person, validationErrors: string[] } = { validationErrors };

  if (validationErrors.length === 0) {
    ret.person = obj as Person;
  }
  return ret;

}

declare const x: object;
const validatedPerson = validatePerson(x);

if (validatedPerson.person) {
  const b: Person = validatedPerson.person
} else {
  throw Error(`invalid person: ${validatedPerson.validationErrors.join(', ')}`)
}

Итак, вы используете validatedPerson.person в качестве защиты типа, и он автоматически сужается для вас.


Еще более краткое решение - сделать то, что предложил @TitianCernicova-Dragomir, и вернуть либо Person, либо массив ошибок:

function validatePerson(obj: object) {
  const validationErrors = ["dependencies", "name"]
    .filter(key => key in obj)
    .map(key => `key ${key} is missing from obj`);
  return (validationErrors.length) ? validationErrors : (obj as Person);
}

declare const x: object;
const validatedPerson = validatePerson(x);

if (!Array.isArray(validatedPerson)) {
  const b: Person = validatedPerson;
} else {
  throw Error(`invalid person: ${validatedPerson.join(', ')}`)
}

Теперь тип возврата validatePerson()Person | string[]. Затем вы используете, является ли возвращаемый тип массивом или нет, в качестве защиты типа, и все это работает.


Надеюсь, они подскажут вам, как двигаться дальше. Удачи!

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