Как связать типы предикатов этих типов с типами их параметров?

Вот чего я хочу добиться:

function isString<T>( value: T ): value is T extends string ? T : string {

  return typeof value === "string";

}
function isNotString<T>( value: T ): T extends string ? unknown : T {

  return typeof value !== "string";

}

Но я получаю сообщение об ошибке: «Тип предиката типа должен быть назначен типу его параметра». что и ожидается.

Кстати, я не хочу добиться следующего:

function isString<T>( value: T ): Extract<T, string> {

  return typeof value === "string";

}
function isNotString<T>( value: T ): Exclude<T, string> {

  return typeof value !== "string";

}

Потому что оба из них могут вывести never тип, который мне не нужен. Тем не менее, мне всегда нужно иметь дело с известным типом.

Не могли бы вы помочь мне добиться этого с помощью TypeScript? :-)


Ответить на ответы

Спасибо вам большое за содержательные ответы, ребята. Вот мои ответы:

@ Линда Пайсте

Не беспокойтесь о создании защиты типа isNotString, потому что она не будет делать то, что вы хотите. Вместо этого проверьте, что isString имеет значение false, и машинописный текст все правильно выведет.

Вы видите что-то не так со следующей функцией? Если я не ошибаюсь, я думаю, что он достигает того, чего я ожидал.

function isNotString<T>( value: T ): value is Exclude<T, string> {
    return typeof value === "string";
}

Мне лично нравится включать общий тип и утверждать, что значение - это T & string, а не просто value is string, чтобы никакая информация не терялась, если тип уже известен как конкретное подмножество строки.

Мне нравится этот подход, но раньше я добивался этого странным образом. Я не собираюсь рассказывать об этом здесь, так как это был безумный подход.

У нас не было никогда в нашем операторе возврата, но мы по-прежнему получаем никогда в нашем коде, потому что это единственный логический тип, если наша переменная a: string не является строкой.

Не могу с этим согласиться. Вчера я точно был не в своем уме. :-)

Стоит отметить, что встроенные средства защиты типов в машинописном тексте на самом деле не уточняют тип в отрицательном случае. Это просто показывает, что вы не можете делать это должным образом, имея дело с неизвестным типом, таким как T.

Я согласен, дорогой. Я просто хочу иметь это для подробностей. Может я ошибаюсь, но isNotString мне кажется более многословным, чем ! isString.

@Andrei Tătar

Вы можете, пожалуйста, взгляните на это Детская площадка?

Я вижу, что Exclude работает, как ожидалось, но value: T | string - нет. Не стесняйтесь указать, если я что-то пропустил.


person A.N.M. Saiful Islam    schedule 07.02.2021    source источник


Ответы (3)


Короткий ответ

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

Длинный ответ

@ Ответ Андрея верен, что охранник типа должен подтвердить свой возврат с помощью value is. Я хочу заняться проблемой never. Типовой страж - это _5 _ / _ 6_ проверка. Одна из этих веток проверяется, а другая нелогична, поэтому будет never.

Допустим, это наш охранник типа. Мне лично нравится включать общий тип и утверждать, что value is T & string, а не просто value is string, чтобы никакая информация не терялась, если тип уже известен как конкретное подмножество string.

function isString<T>( value: T ) : value is T & string {
  return typeof value === "string";
}

Теперь мы хотим использовать эту защиту типа в операторе if. Посмотрим, что произойдет, если мы сделаем это с переменной, тип которой уже известен.

const a: string = "something";

if (isString(a)) {
    console.log(a); // `a` here is `string`
} else {
    console.log(a); // `a` here is `never` 
}

У нас не было never в нашем операторе возврата, но мы все равно получаем never в нашем коде, потому что это единственный логический тип, если наша a: string переменная не string.

Определение isNotString typeguard на самом деле не обязательно, потому что вы могли бы с таким же успехом проверить ! isString(). Но если мы захотим это сделать, он вернет never при вызове со строкой. Все остальное не имело бы смысла. Утверждение применяется только тогда, когда функция истинна. Итак, если string не string, тогда что это?

Честно говоря, действительно сложно определить охранник типа, который утверждает отрицательный случай, т. Е. value is not string, потому что охранники типа предназначены для утверждения положительного. Exclude кажется, что он должен работать, но он не может различать объединения, потому что объединение не расширяется string, поэтому оно всегда возвращает never. Я понимаю, почему это нежелательное поведение, которого вы не хотите. Так что просто не делай этого. У вас уже есть охранник, который видит, является ли что-то string, поэтому используйте его, чтобы узнать, не string, и он будет делать то, что вы хотите.

function checkUnion(value: string | number) {
    if (!isString(value)) {
        console.log(value); // `value` is `number`
    }
    if (isString(value)) {
        console.log(value) // `value` is `string`
    }
}

Стоит отметить, что встроенные средства защиты типов в машинописном тексте на самом деле не уточняют тип в отрицательном случае. Это просто показывает, что вы не можете сделать это должным образом, имея дело с неизвестным типом, таким как T.

function checkGeneric<T>(value: T) {
    if ( typeof value !== "string" ) {
        console.log(value); // value is still just T
    }
    if ( typeof value === "string" ) {
        console.log(value); // value is T & string
    }
}

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

person Linda Paiste    schedule 07.02.2021
comment
@ Linda Paiste не стесняйтесь проверять мои ответы под вопросом. Большое вам спасибо за ваш ответ. :-) - person A.N.M. Saiful Islam; 08.02.2021

Вы могли бы сделать это так:

function isNotString<T>(value: T | string): value is T {
    return typeof value !== "string";
}

для проверки строки вы можете просто:

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

}
person Andrei Tătar    schedule 07.02.2021
comment
Спасибо за ваш вклад @Andrei. Я вижу, что вы полностью удалили поддержку универсального типа из isString, а поддержка универсального типа для isNotString также не достигает того, что я планировал. Вы еще что-нибудь имеете в виду? :-) - person A.N.M. Saiful Islam; 07.02.2021
comment
@ A.N.M.SaifulIslam, возможно, вам стоит точно объяснить, чего вы пытаетесь достичь (какой вариант использования вы ищете с примерами). - person Andrei Tătar; 07.02.2021
comment
@ Андрей Татар не стесняйтесь проверять мои ответы под вопросом. :-) - person A.N.M. Saiful Islam; 08.02.2021

Итак, наконец, я пришел к следующему:

function isString<T>( value: T ): value is T & string {
    return typeof value === "string";
}
function isNotString<T>( value: T ): value is Exclude<T, string> {
    return typeof value === "string";
}

площадка.

person A.N.M. Saiful Islam    schedule 08.02.2021
comment
Я не думаю, что это правильно. Вы проверяете, является ли тип строкой, но она возвращает _1 _... - person Andrei Tătar; 08.02.2021
comment
Ну разве это не пересекает T и string? А что насчет Extract<T & string, string>? - person A.N.M. Saiful Islam; 08.02.2021