#reactjs #typescript #generics
Вопрос:
Вопрос
У меня есть модель редуктора с действием, которое принимает payload
ожидаемую форму { id: string, newValues: Partial<A_or_B> }
. В приведенном ниже коде принудительное применение типа A_or_B
не применяется при newValues
объявлении встроенного; однако, если я объявляю newValues
заранее и назначаю ему свойства, ограничения применяются. Как я могу применить ограничения A_or_B
даже в том случае, если newValues
он объявлен встроенным?
Проблемный Код
import { useContext } from "react";
import { AppDispatchContext } from "../src/AppProvider";
import { ActionTypes, A_or_B } from "./interfaces";
export default function App() {
const dispatch = useContext(AppDispatchContext);
const x: Partial<A_or_B> = {};
// Property 'color' does not exist on type
// 'Partial<A> | Partial<B>'.
// Property 'color' does not exist on type 'Partial<A>'.
x.color = "red";
dispatch({
type: ActionTypes.POINT_UPDATED,
// why can I assign a color to Partial<A>|Partial<B> if 'color'
// only exists on B?
payload: { id: "x", newValues: { color: "red" } }
});
}
Ссылка на CodeSandbox
Комментарии:
1. Для справки, вот более простой случай typescriptlang.org/play?strictPropertyInitialization=false#code/… Это та же самая проблема?
2. @Алекс Уэйн Вроде как…
Ответ №1:
Это распространенная проблема, с которой большинство людей сталкиваются при работе с union
типами в typescript. Некоторые относятся к ним так, как если бы они были типами пересечения, в то время как некоторые хотели бы, чтобы они вели себя скорее как взаимоисключающие типы.
Однако typescript имеет две различные интерпретации типов объединений, в зависимости от вашего варианта использования.
Свойства
При попытке доступа к свойству типа объединения вы можете получить доступ только к свойствам, которые являются общими для всех членов объединения. Другой способ сказать это состоит в том, что у вас есть доступ только к пересечению свойств членов союза.
Это может быть полным ртом, но документы typescript дают хорошую аналогию, чтобы объяснить это:
Например, если бы у нас была комната с высокими людьми в шляпах и другая комната с испанцами, говорящими в шляпах, после объединения этих комнат единственное, что мы знаем о каждом человеке, — это то, что он должен быть в шляпе.
Таким образом, в случае доступа к свойствам typescript разрешает доступ только к подмножеству свойств (без каких-либо дополнительных подсказок от пользователя), образуя, таким образом, пересечение.
Членство (он же вывод типа)
Когда дело доходит до вывода типа, компилятор довольно либерально относится к тому, что он позволяет сопоставлять тип объединения. По сути, это говорит о том, что если вы хотите быть частью этой группы, вам просто нужно выглядеть как один из членов этой группы. В этом случае вам не обязательно иметь свойство, соответствующее свойству каждого члена союза.
Возможно, решение вашей проблемы состоит в том, чтобы просто использовать тип пересечения для x
:
const x: Partial<Aamp;B> = {};
x.color = "red"; // works!
...
newValues: x; // also works
Это решает проблему как доступа к собственности, так и членства
Ответ №2:
Ограничения на самом деле применяются в вашей полезной нагрузке. Вы можете проверить это, добавив свойство в свой полезный груз, которого нет в типе A или B.
Реальная проблема здесь заключается в выводе типов.
const x: Partial<A_or_B> = {};
// Property 'color' does not exist on type
// 'Partial<A> | Partial<B>'.
// Property 'color' does not exist on type 'Partial<A>'.
x.color = "red";
x
присваивается допустимое значение объединения, но Typescript не может определить, относится ли оно к типу A или B, поэтому x.color
его нельзя проверить, поскольку TS считает, что существует вероятность того, что оно может быть типа B.
payload: { id: "x", newValues: { color: "red" } }
newValues
явно устанавливается для объекта, который содержит только color
то, что означает, что Typescript знает, что это должно быть типа A.