#typescript
#typescript
Вопрос:
Я создал надуманный пример (Typescript Playground), чтобы попытаться проиллюстрировать мою проблему. foo
, bar
и baz
являются взаимоисключающими. Я только ищу решение, которое сохраняет XYZ
в качестве типа параметр моей функции. Я уже понимаю, что этот тип X
здесь подошел бы.
type X = { foo: string; bar?: undefined; baz?: undefined }
type Y = { foo?: undefined; bar: string; baz?: undefined }
type Z = { foo?: undefined; bar?: undefined; baz: string; }
type XYZ = X | Y | Z;
function foo(xyz: XYZ): string | undefined {
return xyz.foo;
}
В идеале мне нужно было бы определить только требуемые части:
type X = { foo: string };
type Y = { bar: string };
type Z = { baz: string };
Но без избыточности я получаю это сообщение об ошибке:
Property 'foo' does not exist on type 'XYZ'.
Property 'foo' does not exist on type 'Y'.
Я пробовал это, но в итоге получаю типы, которые выглядят как undefined amp; string
:
type XYZ = { foo?: undefined; bar?: undefined; baz?: undefined } amp; (X | Y | Z);
Комментарии:
1.Если это так
foo
, вы говорите, что этоbar
должно бытьundefined
? Я не думаю, что вы можете сделать это с одним типом, но я не Тициан Черникова-Драгомир, так что…2. По крайней мере, текущий тип не позволяет создать объект, который имеет оба
foo
иbar
. Я не уверен, что смогу ввести narrow в теле функции.3. «Я не уверен, что смогу ввести narrow в теле функции». Должен быть какой-то способ (даже если это проверка свойств, как в моем ответе), иначе как вы пишете логику функции?
4. В этом примере похоже, что форма
XYZ
является{ foo: string, bar: string, baz: string }
, поэтому у меня просто не было безопасности типов, о которой я думал, что у меня есть.
Ответ №1:
Я думаю, вы ищете amp;
(тип пересечения), а не |
(тип объединения):
type X = { foo: string };
type Y = { bar: string };
type Z = { baz: string };
type XYZ = X amp; Y amp; Z;
Из документации по типам пересечений:
Тип пересечения объединяет несколько типов в один. Это позволяет вам объединить существующие типы, чтобы получить единый тип, обладающий всеми необходимыми функциями. Например,
Person amp; Serializable amp; Loggable
этоPerson
иSerializable
иLoggable
. Это означает, что объект этого типа будет содержать все элементы всех трех типов.
Тем не менее, если вы говорите, что когда foo
существует, bar
и baz
должно быть неопределенным, я думаю, вы бы придерживались типа пересечения, но тогда вам нужно сообщить TypeScript, что вы знаете о foo
существовании, прежде чем использовать его, выполнив утверждение типа. Потому что в противном случае он не может знать, что вы имеете дело с X
, а не с Y
or Z
. Например.:
type X = { foo: string };
type Y = { bar: string };
type Z = { baz: string };
type XYZ = X | Y | Z;
function foo(xyz: XYZ): string | undefined {
if ("foo" in xyz) { // Or whatever appropriate check
return (xyz as X).foo;
}
return undefined;
}
Я не думаю, что есть способ сделать это без утверждения типа. (Но опять же, я не Тициан Черникова-Драгомир. 🙂 )
Другой вариант — использовать перегрузки функций, но вы конкретно сказали, что хотите сохранить XYZ
, и для этого все равно потребуется наличие логики для определения того, с чем вы имеете дело, с утверждениями типа.
Комментарии:
1. Я надеялся сохранить
foo
,bar
иbaz
взаимоисключающие. Я только что понял, что даже в моем примереif (xyz.foo)
не сужаетсяxyz.bar
доundefined
.2. @Wex — Хех, я только что понял, что это может быть то, что вы искали, и обновлял. 🙂