Почему объединяющий оператор nulli sh не работает как typeguard в машинописи? - PullRequest
1 голос
/ 15 апреля 2020

С Typescript 3.7 был введен nulli sh оператор объединения . Казалось бы, это идеальный тип защиты для таких случаев, как

const fs = (s: string) => s
const fn = (n: number) => n

let a: string | null | undefined
let b: number | null | undefined

const x = (a ?? null) && fs(a)
const y = (b ?? null) && fn(b)

Но если вы поместите этот код в typcript * , он предупредит вас об обоих параметрах a a b, переданных в fs / Функции fn, такие как:

Argument of type 'string | null | undefined' is not assignable to parameter fo type 'string' Я немного поэкспериментировал и обнаружил, что это не только проблема, связанная с оператором слияния nulli sh, но и не мог сойти с ума когда машинопись может использовать что-то в качестве охранника, а когда нет (ниже вы найдете некоторые примеры )

Две последние строки меня смущали больше всего. Мне кажется, что оба выражения, назначенные для x7 и x8, были бы полностью эквивалентны, но хотя в выражении, назначенном для x8, работает typeguard, это не подходит для машинописи в выражении x7:

const fs = (str: string) => str
const create = (s: string) => s === 's' ? 'string' : s === 'n' ? null : undefined
const a: string | null | undefined = create('s')
const b: string | null | undefined = 's'
let x
if (a !== null && a !== undefined) {
    x = a
} else {
    x = fs(a)
}
const x1 = a !== null && a !== undefined && fs(a)
const x2 = a !== null && a !== void 0 && fs(a)
const x3 = (a ?? null) && fs(a)
const x4 = (b ?? null) && fs(b)
const x5 = a !== null && a !== undefined ? a : fs(a)
const something = a !== null && a !== undefined
const x6 = something ? a : fs(a)
const x7 = something && fs(a)
const x8 = (a !== null && a !== undefined) && fs(a)

Я не уверен, если машинописный текст просто неспособен применить защиту типов по какой-либо причине или это действительно ошибка в машинописи. Так есть ли книга правил, когда машинописный текст может применять охрану шрифтов, а когда нет? Или это может быть ошибка? Или есть какая-то другая причина, по которой я не могу скомпилировать эти примеры?

Кстати. при использовании определяемой пользователем защиты типов, конечно, она отлично работает, но было бы неплохо не добавлять некоторый код времени выполнения для работы защиты типов.

1 Ответ

1 голос
/ 15 апреля 2020

Я потратил много времени, пытаясь написать механическое объяснение того, почему определенные выражения, такие как expr1 || expr2 && expr3, действуют в качестве защитников типов в определенных ситуациях, а не в других. Это закончилось тем, что стало несколькими страницами и все еще не учитывало все случаи в ваших примерах. Если вам интересно, вы можете посмотреть код, реализованный для операторов выражений в microsoft / TypeScript # 7140 .


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

Компилятор может выполнить этот анализ все время , для каждого возможного выражения с типом union, с которым оно сталкивалось. Мы могли бы назвать это «автоматизированным c анализом потока распределенного управления», и было бы полезно почти всегда создавать желаемое поведение защиты типа. Недостатком является то, что компилятору потребуется больше памяти и времени, чем вы готовы потратить, и, возможно, больше, чем человечество может потратить из-за комбинаторного взрыва, который происходит, поскольку каждое дополнительное выражение типа объединения оказывает мультипликативный эффект на требуемые Ресурсы. Алгоритмы экспоненциального времени не годятся для хороших компиляторов.

Иногда я хотел иметь возможность намек на то, что конкретное значение типа объединения в определенной области видимости должно проанализировать таким образом, и я даже подал запрос на такой «анализ потока распределенного управления opt-in» (см. microsoft / TypeScript # 25051 ), но даже это потребовало бы больших усилий по разработке для реализации и будет отклоняться от целей проектирования TS по включению JS шаблонов проектирования, не требуя от разработчика слишком тщательного анализа потока управления.

Итак, в итоге разработчики языка TypeScript реализуют эвристику, которая выполняет такой анализ в ограниченных областях, которые позволяют обычные и идиоматические c JavaScript шаблоны кодирования. Если код, подобный (a ?? null) && fs(a), не считается идиоматическим c и достаточно традиционным для разработчиков языков (это частично субъективно и частично зависит от изучения корпуса реального кода), и если его реализация приведет к большой производительности компилятора штраф, тогда я не ожидал бы, что язык его поддержит.

Некоторые примеры:

  • microsoft / TypeScript # 12184 : поддержка «сохранения» msgstr "результат защиты типа в константу (как ваш пример something) для дальнейшего использования. Это открытое предложение, помеченное как «пересмотр» со зловещим провозглашением языковым архитектором, что было бы трудно сделать это в качественной форме. Это может быть идиоматизм c, но это может быть сложно реализовать эффективно.

  • microsoft / TypeScript # 37258 : поддержка последовательных защит типа, в которых выполняются сужения несколько коррелированных переменных одновременно. Он закрыт как слишком сложный, потому что, чтобы избежать алгоритма экспоненциального времени, вам нужно жестко закодировать его с небольшим количеством проверок, что в целом не очень полезно. Предложение от сопровождающих языка: используйте больше идиоматических c проверок.


Так что это самый близкий к этому официальный или официальный ответ. Надеюсь, поможет; удачи!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...