I'm trying to define re-usable React ponents that accept a React.Context field through a prop. They may not need the entirety of the properties available in the parent's context and, given the desire for reuse, may be encapsulated in Providers with different Context structures (but the same core properties needed by the reused sub ponent). For instance a parent provider higher in the tree may define the Context type like so:
type SuperSet = {
x: number,
y: number,
z: number
}
let superSet = {x: 1, y: 2, z: 3}
const SuperSetContext = React.createContext<SuperSet>(superSet)
const SuperSetProvider = (props) => {
return (
<SuperSetContext.Provider value={superSet}>
...
{/* Arbitrarily deep nested ponent in the tree, most likely in a different file*/}
<SubComponent Context={SuperSetContext} />
</SuperSetContext.Provider>
);
}
The SubComponent should (I believe) be able to define a Context prop with less properties like so
const SubComponent: React.FunctionComponent<{
Context: React.Context<{x: number, y: number}>
}> = ({ Context }) => {
const { x, y } = useContext(props./Context);
return (<div>{x + y}</div>)
}
Or via Pick<>
Context: React.Context<Pick<SuperSet, 'x' | 'y'>>
However either way the above SubComponent causes a type error when the prop is assigned within the Provider
<SubComponent Context={SuperSetContext} />
Type 'Context<SuperSet>' is not assignable to type 'Context<SubSet>'.
Types of property 'Provider' are inpatible.
Type 'Provider<SuperSet>' is not assignable to type 'Provider<SubSet>'.
Types of parameters 'props' and 'props' are inpatible.
Type 'ProviderProps<SubSet>' is not assignable to type 'ProviderProps<SuperSet>'.ts(2322)
test.tsx(26, 3): The expected type es from property 'Context' which is declared here on type 'IntrinsicAttributes & { Context: Context<SubSet>; } & { children?: ReactNode; }'
I created a Typescript Playground to test it without jsx but it occurs regardless of using jsx. Additionaly I don't see the same behavior with naive generic classes/functions.
So is there a way to define the SubComponent's Context definition with a subset or Context properties OR a different paradigm to acplish the same design and escape this particular typing mismatch?
I'm trying to define re-usable React ponents that accept a React.Context field through a prop. They may not need the entirety of the properties available in the parent's context and, given the desire for reuse, may be encapsulated in Providers with different Context structures (but the same core properties needed by the reused sub ponent). For instance a parent provider higher in the tree may define the Context type like so:
type SuperSet = {
x: number,
y: number,
z: number
}
let superSet = {x: 1, y: 2, z: 3}
const SuperSetContext = React.createContext<SuperSet>(superSet)
const SuperSetProvider = (props) => {
return (
<SuperSetContext.Provider value={superSet}>
...
{/* Arbitrarily deep nested ponent in the tree, most likely in a different file*/}
<SubComponent Context={SuperSetContext} />
</SuperSetContext.Provider>
);
}
The SubComponent should (I believe) be able to define a Context prop with less properties like so
const SubComponent: React.FunctionComponent<{
Context: React.Context<{x: number, y: number}>
}> = ({ Context }) => {
const { x, y } = useContext(props./Context);
return (<div>{x + y}</div>)
}
Or via Pick<>
Context: React.Context<Pick<SuperSet, 'x' | 'y'>>
However either way the above SubComponent causes a type error when the prop is assigned within the Provider
<SubComponent Context={SuperSetContext} />
Type 'Context<SuperSet>' is not assignable to type 'Context<SubSet>'.
Types of property 'Provider' are inpatible.
Type 'Provider<SuperSet>' is not assignable to type 'Provider<SubSet>'.
Types of parameters 'props' and 'props' are inpatible.
Type 'ProviderProps<SubSet>' is not assignable to type 'ProviderProps<SuperSet>'.ts(2322)
test.tsx(26, 3): The expected type es from property 'Context' which is declared here on type 'IntrinsicAttributes & { Context: Context<SubSet>; } & { children?: ReactNode; }'
I created a Typescript Playground to test it without jsx but it occurs regardless of using jsx. Additionaly I don't see the same behavior with naive generic classes/functions.
So is there a way to define the SubComponent's Context definition with a subset or Context properties OR a different paradigm to acplish the same design and escape this particular typing mismatch?
Share Improve this question asked Apr 16, 2020 at 3:19 ZacharyZachary 852 silver badges11 bronze badges2 Answers
Reset to default 4If you declare you own interface that extends React.Context<T>
, Typescript will accept it.
interface MyContext<T> extends React.Context<T> {} // this does the trick
const SubComponent: React.FunctionComponent<{
// use MyContext instead of React.Context
Context: MyContext<{x: number, y: number}>
}> = ({ Context }) => {
const { x, y } = React.useContext(Context);
return <div>{x + y}</div>;
};
// Now pass a SuperSet
type SuperSet = {
x: number;
y: number;
z: number;
};
let superSet = { x: 1, y: 2, z: 3 };
const SuperSetContext = React.createContext<SuperSet>(superSet);
const SuperSetProvider = props => {
return (
<SuperSetContext.Provider value={superSet}>
{/* No TS error! */}
<SubComponent Context={SuperSetContext} />
</SuperSetContext.Provider>
);
};
you can check it out in this sandbox
To be honest, I can't explain what causes the difference in behavior.
In this case you should use a generic ponent (SubComponent
), since TS is right here: you can't assume React.Context<SubSet>
is a subset of React.Context<SuperSet>
because they're boxed types. Using a simple generic ponent, you can work around this specifying what you really want: that the context type should be a subset:
function SubComponent<T extends { x: number, y: number }>(props: {
context: React.Context<T>
}) {
const { x, y } = React.useContext(props.context);
return (<div>{x + y}</div>)
}
You can see full example in the playground: Playground Link
发布者:admin,转转请注明出处:http://www.yc00.com/questions/1745459460a4628640.html
评论列表(0条)