#typescript #typescript-typings
#typescript #typescript-типизации
Вопрос:
У меня возникла проблема при попытке правильно ввести одну из моих функций. В следующем коде тип x
является any
, и я хотел бы ввести его лучше, чем это.
interface Pet {
name: string;
}
const checkType = (x: any): x is Pet => {
return 'name' in x amp;amp; typeof x.name === 'string';
}
Я понимаю, что лучше всего подойдет unknown
или object
, но оба они также выдают мне ошибку
interface Pet {
name: string;
}
const checkType = (x: unknown): x is Pet => {
return 'name' in x amp;amp; typeof x.name === 'string';
}
Объект имеет тип «неизвестный»
interface Pet {
name: string;
}
const checkType = (x: object): x is Pet => {
return 'name' in x amp;amp; typeof x.name === 'string';
}
Свойство ‘name’ не существует для типа ‘object’
Итак, мой вопрос в том, как я могу правильно печатать x
без приведения к any
?
Следующее может быть решением, но я нахожу его слишком большим и специфичным :
interface Pet {
name: string;
}
const checkType = (x: object): x is Pet => {
return 'name' in x amp;amp; typeof (x as {
name: unknown,
}).name === 'string';
}
Дополнительная информация :
Пример с any
, который может вызвать проблему :
Комментарии:
1. это немного зависит от того, как выглядят фактические объекты, которые вы хотите поместить в эту функцию, если все они являются просто картами объектов, это может быть что-то вроде
const checkType = (x: {[key: string]: any}): x is Pet => { return 'name' in x amp;amp; typeof x.name === 'string'; }
2. Это определенно может быть решением. Но опять же, мы используем
any
, я бы предпочел использоватьconst checkType = (x: {[key: string]: unknown}): x is Pet => ...
. Но когда я это делаю, я получаю другую ошибкуA type predicate's type must be assignable to its parameter's type. Type 'Pet' is not assignable to type '{ [key: string]: unknown; }'. Index signature is missing in type 'Pet'.
3. Я думаю, что одна проблема заключается в том, что
in
работает только для сужения союзов — «гдеn
— строковый литерал или тип строкового литерала, аx
— тип объединения» . Еслиx: any
, вы можете простоreturn typeof x.name === 'string'
, и компилятор будет доволен.
Ответ №1:
Я бы рекомендовал решить это одним из двух способов:
- Определите объединение возможных типов ввода
- Определите тип ключа / значения
Первый — это подход, использованный в примере из Typescript Handbook (откуда, я полагаю, вы взяли ‘Pet’:
function isFish(pet: Fish | Bird): pet is Fish {
return (pet as Fish).swim !== undefined;
}
Однако, если вы работаете над общей библиотекой, это не удается, потому что вы не будете знать, что ваши потребители будут вам давать, и расширение этого объединения не является веской причиной для кардинального изменения. Затем вы можете использовать метод # 2:
interface IPojo {
[key: string]: any,
}
const checkType = (x: IPojo): x is Pet => {
return 'name' in x amp;amp; typeof (x as {
name: unknown,
}).name === 'string';
};
function foo(p: Pet) {
console.log(p.name);
}
const bar: IPojo = {}
const baz = 'name';
bar[baz] = 'hi';
foo(bar); // TypeError, compiler can't verify bar.name
if (checkType(bar)) {
foo(bar); // No TypeError, type narrowed correctly by your guard
}
Ответ №2:
Я бы использовал часть вашего интерфейса Pet в качестве входных данных для вашего type guard.
type maybePet = Partial<Pet>
const checkType = (x: maybePet): x is Pet => {
return 'name' in x amp;amp; typeof x["name"] === 'string';
}
Это позволяет компилятору помечать не объектно-подобные входные данные, такие как число, например.
let a = {};
if(checkType(a))//allows the type check but will not pass
{
console.log("a")
}
let b = {name:"good", other:1};
if(checkType(b))//allows the type check and passes
{
console.log("b")
}
checkType(3) //typescript complier complains
Мой пример кода немного надуман, если вы измените ключи проверяемого объекта так, чтобы они не соответствовали интерфейсу Pet, компилятор жалуется, поскольку он сделал вывод, что ваш объект не может быть типа Pet, однако, если вы получаете любой объект (или явно приводите его к любому), тогда проверка может продолжаться.
let c = {namey:"good", other:1};
if(checkType(c))//compiler complains because it knows typeof c can't be a Pet
{
console.log("c")
}
let d:any = {namey:"good", other:1};
if(checkType(d))//allows the type check but will not pass
{
console.log("d")
}