В чем разница между generi c, который расширяет тип и обычный тип в Typescript? - PullRequest
2 голосов
/ 07 февраля 2020

Привет эксперты Typescript,

Может кто-нибудь объяснить, почему следующий Код дает мне ошибку в строке 16, но не в 13. Это код или отсутствует?

Код

interface Config {
  // There need to be different types in here for the error to occur
  A: number
  B: string
}

type union = "A" | "B" 

var Global = {A: 1, B: "Hello"} as Config

function foo<K extends union>(key: K, x: union) {
  if (x === "A"){
    Global[x].toFixed()
  }
  if (key === "A"){
    Global[key].toFixed()
  }
}

Playground Link

Ответы [ 2 ]

1 голос
/ 07 февраля 2020

Вероятно, это отсутствующая функция.

Когда вы проверяете (x === "A"), он действует как защита типа для типа значения x, вызывая его сужение от объединения. введите от "A"|"B" до "A" через анализ потока управления .

К сожалению, общие параметры типа c в TypeScript не сужаются при анализе потока управления; см. microsoft / TypeScript # 13995 для получения дополнительной информации. Так, например, проверка (key === "A") будет не сужать тип K до "A". Такое ограничение имеет смысл, когда у вас есть несколько значений одного типа c generi:

function foo<K extends Union>(key: K, x: Union, key2: K) {    
  if (key2 === "A") {
    Global[key].toFixed(); // error!
  }
}

Очевидно, что проверка значения key2 не должна влиять на тип key, поэтому консерватор не считает, что K следует сузить. Это основная проблема c в microsoft / TypeScript # 13995, и были подняты многочисленные связанные с этим вопросы с предложениями о том, как ее решить в тех случаях, когда такое сужение безопасно выполнять. Пока что до сих пор ничего не дошло до языка.


Хотя это не совсем полная история; можно противостоять: хорошо, возможно вы не можете сузить параметр типа K с K extends Union до K extends "A", но , конечно, вы можете сузить тип значения key с K до "A" или K & "A" ( тип пересечения ), что сделает Global[key].toFixed() успешным:

if (key === "A") {
  Global[key as (K & "A")].toFixed(); // okay
}

И сейчас у меня нет хорошего ответа на этот вопрос. Большинство вопросов, которые я видел об этом, в конечном итоге были переданы на microsoft / TypeScript # 13995. 10

Наиболее полный ответ, который я могу получить, - это то, что использование встроенных защитных устройств типа a === b или typeof a === "string" или a instanceof B или a in b приводит только к фильтрации союзов или, возможно, сужение string или number до строковых или числовых литералов, но никогда не создает тип пересечения. Я спрашивал ранее, см. Microsoft / TypeScript # 21732, для защиты типа a in b для создания некоторых пересечений, но она не была реализована. Таким образом, это может быть две отсутствующие функции:

  • без сужающихся обобщенных параметров типа c и
  • без встроенного ограждения типа, сужающегося до пересечений.

Итак, обходные пути: Очевидно, что самый простой способ для этого примера - просто переназначить значение переменной типового типа c в переменную типа объединения:

const k: Union = key;
if (k === "A") {
  Global[k].toFixed();
}

Или вы можете использовать утверждение типа , как указано выше as (K & "A") или просто as "A":

if (key === "A") {
  Global[key as (K & "A")].toFixed(); // okay
  Global[key as "A"].toFixed(); // okay
}

Или, если это часто случается, вы можете написать свой собственный функция защиты определенного пользователем типа функция, поскольку защита определенного пользователем типа do создает пересечения в ветви true следующего потока управления:

const isEq =
  <T extends string | number | boolean>(v: any, c: T): v is T => v === c;

if (isEq(key, "A")) {
  Global[key].toFixed(); // okay, key is now of type K & "A";
}

Hope один из тех, кто помогает вам. Хорошо, удачи!

Детская площадка ссылка на код

0 голосов
/ 07 февраля 2020

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

interface Config {
  // There need to be different types in here for the error to occur
  A: number
  B: string
}

type Union = keyof Config;

const Global: Config = { A: 1, B: "Hello" };

function foo<K>(key: K & Union, x: Union) { // Use intersection type instead of inheritance
  if (x === "A"){
    Global[x].toFixed();
  }
  if (key === 'A'){
    Global[key].toFixed()
  }
}

Playground link

Также см. Этот вопрос: Разница между расширяющимися и пересекающимися интерфейсами в TypeScript?

...