Я пытаюсь сказать TypeScript, что тип значения в первом параметре функции определяет тип, который может иметь второй. По сути, я хочу сузить возможные комбинации типов.
В конкретном примере я хочу, чтобы 2-й параметр был null
, если параметр 1sr является примитивным типом, и я хочу, чтобы он был Set
(объектов), если 1-й является объектом.
Предыстория для тех, кто хочет знать, почему я хочу это сделать: это оптимизация для обнаружения круга рекурсивной stringify
функцией. Клонирование набора seenObjects
для разных ветвей, чтобы разрешить дублирование между ветвями, которые на самом деле не образуют круг, необходимо только в том случае, если объект является объектом.
Все это хорошо работает для функции извне, то есть кода, вызывающего функцию. В приведенном ниже примере четыре вызова stringify
работают именно так, как хотелось бы.
Однако внутри функции условный тип, по-видимому, не имеет никакого эффекта. TS жалуется на то, что 2-й параметр, возможно, равен null
, несмотря на проверку 1-го параметра, и эта проверка в сочетании с условный тип - если он там использовался - ограничивает тип 2-го параметра до Set
, без null
.
Код
Ссылка на TS PlayGround (необходимо включить strictNullChecks)
function isObject (thing: unknown): thing is Record<string, any> {
return typeof thing === 'object' && thing !== null;
}
function stringify<T extends unknown>(
obj: T,
seenObjects: T extends Record<string, any> ? Set<Record<string, any>> : null
): string {
if (isObject(obj)) {
seenObjects.add(obj); // ERROR (bad)
}
return 'The End';
}
// No errors (good)
stringify(42, null);
stringify([], new Set());
// ERRORS (good)
stringify([], null);
stringify(42, new Set());
Вопрос
Я мог бы просто добавить дополнительные проверки уточнения типа и покончить с этим.
Однако мне любопытно, знает ли кто-нибудь способ добиться желаемого результата без добавления кода. В моем фактическом коде значение 2-го параметра жестко запрограммировано в зависимости от 1-го, поэтому мне кажется уместным решить эту проблему с помощью статического анализа типов, а не с помощью дополнительного кода, полностью ненужного запуска во время выполнения.
PS: Интересно, что, поскольку я просто переключаю эту кодовую базу с Flow на TypeScript, в Flow я мог «взломать» Flow, дав ему код с комментариями, например проверки уточнения типов в /*: …. */
, которые Flow интерпретировал бы как «живые». "код. Так что я мог бы успокоить Flow, предоставив ему код, который он хочет видеть для проверки типа, фактически не помещая его в среду выполнения, потому что он находится в комментарии.
Обновление: ошибка ТС?
Когда я меняю условие if
с проверки obj
на if (seenObjects !== null)
, ошибка на seenObjects
("может быть нулевой") по-прежнему остается! Несмотря на то, что этот код теперь стоит за явной проверкой null
?
const
, а затем добавитьas const
в конце. Таким образом, TS рассматривает массив как неизменяемый и принимает типы значений такими, какие они есть, без обобщения. Тогда типы будут выводиться правильно. - person Mörre   schedule 26.04.2019