Введите «предикат типа» без каких-либо

#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:

Я бы рекомендовал решить это одним из двух способов:

  1. Определите объединение возможных типов ввода
  2. Определите тип ключа / значения

Первый — это подход, использованный в примере из 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")
} 
  

Ссылка на игровую площадку