Я пытаюсь сообщить TypeScript, что тип значения в первом параметре функции определяет тип, который может иметь второй. По сути, я хочу сузить возможные комбинации типов.
В конкретном примере я хочу, чтобы 2-й параметр был null
, если параметр 1sr является примитивным типом, и я хочу, чтобы он был Set
(из объектов), если 1-й является объектом.
История вопроса для тех, кто хочет знать, почему я хочу это сделать: Это оптимизация для обнаружения круга рекурсивной функции stringify
. Клонирование набора seenObjects
для разных ветвей, чтобы разрешить дубликаты между ветвями, которые фактически не образуют круг, необходимо, только когда объект является объектом.
Все это хорошо работает для функции извне , то есть код, вызывающий функцию. В приведенном ниже примере четыре вызова stringify
работают точно так, как вам нужно.
Однако внутри функции условный тип, похоже, не имеет никакого эффекта. TS жалуется, что 2-й параметр, возможно, равен null
, несмотря на проверку 1-го параметра, и что проверка вместе с условным типом - если он там использовался - ограничивает тип 2-го параметра до Set
, без null
.
код
TS PlayGround Link (необходимо включить строгие проверки Null)
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, предоставив ему код, который он хочет видеть для проверки типа, фактически не помещая его в среду выполнения, потому что он находится в комментарии.
Обновление: ошибка TS?
Когда я изменяю условие if
с проверки obj
на if (seenObjects !== null)
, ошибка в seenObjects
(«может быть нулевым») все еще остается! Несмотря на то, что этот код стоит за явной проверкой null
?