Меня интересует URL-адрес NodeJS API. В частности, метод url.format(urlObject). Я хотел бы создать функцию, которая проверяет urlObject перед вызовом формата и выдает TypeError, если какая-либо из пар ключ/значение объекта недействительна или «лишняя» (отсутствует в спецификации).
Есть ли способ, помимо типизации TypeScript, добиться этого?
@messerbill Это не скомпилируется. Вы поняли мой вопрос.
он будет скомпилирован из-за того, что URL-адрес будет установлен во время выполнения... в противном случае я неправильно понял ваш вопрос. Хотя вы пытаетесь «проверить» значение urlObject и подтвердить его, ожидая, что некоторые недопустимые данные могут быть отправлены во время выполнения.



![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


Вам будет трудно это сделать, потому что при создании экземпляра URL:
u = new URL('http://example.com/');
и Object.keys(u), и Object.getOwnPropertyNames(u) вернут пустой массив, и даже JSON.stringify(u) здесь вам не поможет, поскольку в результате получается одна строка верхнего уровня, а не JSON-представление объекта.
Таким образом, вы не сможете легко проверить наличие каких-либо дополнительных полей, если только вы вручную не проанализируете вывод util.format(u) или что-то в этом роде, но вам все равно нужно будет проверять ключи объектов (включая неперечислимые свойства) для проверки других объектов. которые не являются экземпляром URL.
Таким образом, наиболее разумным решением было бы проверить, если u instanceof URL, и если это правда, то верить, что все в порядке, но если это ложь, то проверить конкретные свойства.
Но имейте в виду, что url.format() не нужен настоящий экземпляр URL, например. эти две строки имеют одинаковый эффект:
a = url.format(new URL('http://example.com/'));
b = url.format({ protocol: 'http', host: 'example.com', pathname: '/' });
// a === b
Чтобы проверить, имеет ли экземпляр URL (или любой другой объект) следующие свойства и являются ли они строками, как это должно быть в экземпляре URL, вы можете использовать что-то вроде:
const k = [
'href',
'origin',
'protocol',
'username',
'password',
'host',
'hostname',
'port',
'pathname',
'search',
'hash',
];
const validUrl = u => k.filter(k => typeof u[k] === 'string').length === k.length;
Это вернет true для обычных URL экземпляров:
u = new URL('http://example.com/');
console.info(validUrl(u));
но и для других объектов, если они имеют все необходимые поля.
Но имейте в виду, что для url.format() не требуются все поля, как вы можете видеть в примере выше, так что таким образом вы отклоните некоторые значения, которые идеально подходят для url.format(), например:
u = { protocol: 'http', host: 'example.com', pathname: '/' };
Подвести итог:
Реальные экземпляры URL не показывают вам, какими свойствами они обладают, поэтому вам будет нелегко проверить наличие дополнительных свойств, но я бы не советовал делать это в любом случае, потому что ваш код сломается, если URL когда-либо будет расширен новыми свойствами, что может произойти в будущее.
Чтобы увидеть, есть ли у него все необходимые (по вашему мнению — не обязательно по мнению url.format()!) свойства, вы можете написать простую функцию проверки, но тогда вы не сможете опустить пустые свойства, такие как username, если их нет, и вы придется явно передавать пустые строки для всех пустых свойств, если вы хотите иметь возможность использовать URL-подобные объекты, которые в остальном совместимы с url.format().
Возможно, вы хотите протестировать только некоторые поля, такие как протокол, хост и имя пути, но тогда вам придется смириться с тем, что url.format() примет либо host, либо hostname, предпочитая host, если доступны оба и т. д.
Вы также можете попробовать такой хак:
const testUrl = u => {
try {
new URL(url.format(u));
} catch (e) {
throw new TypeError('your error message');
}
};
и иметь функцию, которая экспериментально проверяет, работает ли данное значение с url.format(), предоставляя URL-адрес, который может быть проанализирован new URL() следующим образом:
testUrl(new URL('http://example.com')); // ok
testUrl({host: 'example.com', protocol: 'http'}); // ok
testUrl({host: 'example.com'}); // throws a TypeError
Теперь в вашей функции, которая вызывает url.format(urlObject), все, что вам нужно сделать, это добавить: testUrl(urlObject) в предыдущей строке, и она будет генерировать описанные вами исключения.
А вообще это сложный вопрос. Вы можете сделать это разными способами, но все они имеют некоторые недостатки. Экземпляры URL на самом деле довольно странные, и даже Джой ломается на них!
Например:
!joi.object({ host: joi.string().required() }).validate({ host: 'example.com' }).error;
// returns true
!joi.object({ host: joi.string().required() }).validate({ nohost: 'example.com' }).error
// returns false
!joi.object({ host: joi.string().required() }).validate(new URL('http://example.com/')).error;
// this throws an exception!
// TypeError: Cannot read property 'host' of undefined
// WTF???
Если даже Джой не может проверить экземпляры URL, значит, их не так просто проверить.
Надеемся, что некоторые из приведенных выше примеров сделают то, что вам нужно.
даже машинописный текст не будет «проверять» эти объекты. Это предотвратит вызов атрибутов только для объекта, который не входит в данный тип.