Почему тип предиката типа должен быть назначен типу его параметра?

У меня есть предикат типа:

// tslint:disable-next-line:no-any
const isString = (value: any): value is string {
  return typeof value === 'string'
}

Это работает, но требует, чтобы я отключил линтер. Я бы предпочел это:

const isString = <T>(value: T): value is string {
  return typeof value === 'string'
}

Таким образом, типом является не any, а вместо этого у нас есть по одной функции защиты типов для каждого типа, то есть по одной функции a -> Boolean для каждого a.

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

A type predicate's type must be assignable to its parameter's type. Type 'string' is not assignable to type 'T'.

Для меня это не имеет смысла ... Почему это должно иметь значение, какой у предиката типа тип?

Вместо этого вы можете использовать (value: {}): value is string => { ... }. Что до ошибки, то она странная; это кажется обратным. Для меня было бы больше смысла назначать T на string. Например, TypeScript должен знать, что Date никогда не будет назначен string, поэтому такой предикат не имеет смысла.

cartant 19.05.2018 04:03

Хм ... но позволять нам использовать (value: any) => value is string в качестве предиката типа - это еще хуже, не так ли? Я могу передать ему Date, если захочу.

Ziggy 19.05.2018 08:52

Это немного другое. Использование T больше похоже на написание целого набора функций. Попробуйте написать защиту типа, определяемую пользователем, например (value: Date): value is string => { ... }, и вы поймете, что я имею в виду.

cartant 19.05.2018 09:12

Хороший момент, универсальный означает «дайте мне по одной из каждой функции a -> Boolean», поэтому, если какая-либо из этих функций не существует, то универсального не существует. Хороший! Не могли бы вы написать ответ?

Ziggy 19.05.2018 10:57
Зод: сила проверки и преобразования данных
Зод: сила проверки и преобразования данных
Сегодня я хочу познакомить вас с библиотекой 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 для повышения производительности приложения путем загрузки модулей только тогда, когда они...
12
4
4 965
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

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

Защита определяемого пользователем типа выполняет проверку во время выполнения, чтобы определить, удовлетворяет ли значение определенного типа предикату типа.

Если нет связи между типом значения и типом в предикате типа, защита не будет иметь смысла. Например, TypeScript не допускает такой пользовательской защиты:

function isString(value: Date): value is string {
    return typeof value === "string";
}

и вызовет эту ошибку:

[ts] A type predicate's type must be assignable to its parameter's type.
Type 'string' is not assignable to type 'Date'.

Значение Date никогда не будет string, поэтому защита бессмысленна: его проверка времени выполнения не нужна и всегда должна возвращать false.

Когда вы указываете общую, определяемую пользователем защиту типа, T может быть чем угодно, поэтому - как и в случае с Date - для некоторых типов защита типа не имеет смысла.

Если вы действительно не хотите использовать any, вы можете использовать вместо него пустой интерфейс - {}:

function isString(value: {}): value is string {
    return typeof value === "string";
}

Если вы также хотите разрешить передачу значений null и undefined охраннику, вы можете использовать:

function isString(value: {} | null | undefined): value is string {
    return typeof value === "string";
}

Что касается сообщения об ошибке, тип предиката должен быть назначен типу значения, поскольку защита типа используется для проверки того, действительно ли значение с менее конкретным типом является значением с более конкретным типом. Например, рассмотрим этого охранника:

function isApe(value: Animal): value is Ape {
    return /* ... */
}

Ape можно присвоить Animal, но не наоборот.

Добавляя к принятому ответу, если вам понадобится использовать защиту типа от миксина, вы также получите эту ошибку, поскольку оператор is ведет себя не так, как implements.

interface Animal { ... }

interface Climber { ... }

interface Ape extends Animal, Climber { ... } 

const isClimberMixin = (animal: Animal): animal is Climber => ...

Такой код не работает, потому что Climber не может быть назначен на Animal, поскольку он не расширяется от него.

Решение, если вы не можете избежать паттерна миксина, состоит в том, чтобы использовать тип объединения:

const isClimberMixin = (animal: Animal): animal is Animal & Climber => ...

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