Repository navigation
Partial object application/"outersection" types #6895
Description
I've searched for how to do this and if there are any open issues requesting it, but couldn't find anything. Apologies if I didn't search enough and there is an issue somewhere.
What I'd like to do is essentially the opposite of intersection types. I want to subtract one type from another, and get the delta as a new type.
Here's a code example:
type X = {
prop1: string;
prop2: string;
prop3: string;
}
type Y = {
prop1: string;
}
function giveMeX(x: X) {
}
function partiallyApplyGiveMeX(y: Y) {
return (z: X - Y) => giveMeX(Object.assign(y, z));
}
var y: Y = {
prop1: ''
};
var wrappedGiveMeX = partiallyApplyGiveMeX(y);
// Don't have to pass prop1 to wrappedGiveMeX
var x = wrappedGiveMeX({prop2: '', prop3: ''});
The type annotation z: X - Y is something I just made up, but what it does is almost the opposite or z: X & Y.
The real world use case I have for this is that in the React world, it's very common to wrap components in other components. I might have one component which expects five props, but when I use that component I don't want to have to specify all of them. Instead I would wrap that component in another component that would pass some of the props to the inner component. And I would like the wrapping component to be typed as a component that expects the other props.
Here's a simple example:
type X = {
prop1: string;
prop2: string;
prop3: string;
}
type Y = {
prop1: string;
}
function InnerComponent(props: X) {
return (
<div></div>
);
}
function wrapComponent<T1, T2>(Component: React.StatelessComponent<T1>, fn: () => T2) {
return (props: T1 - T2) => {
return <Component {...props} {...fn()} />;
};
}
var WrappedComponent = wrapComponent(InnerComponent, () => ({prop1: ''}));
// Now I don't have to pass prop1 to WrappedComponent
var jsx = <WrappedComponent prop2="" prop3="" />;
A question is what happens if the subracting type isn't a strict subset of the other type. I'd say that it would be a compile error and that the right side type has to be a subset of the left type.
Another thing is that it shoould be composable. I could say x: X - Y - Z which would subtract left to right like this x: ((X - Y) - Z).
Activity
andersekdahl commentedon Feb 16, 2016
Here's another use case:
In some cases you want to extend an existing type, but you want to change the type of one or more properties. Something like:
The real world use case I have for this is that I want to define a React component that takes all props that an
<input>does, but I want to redefine what's allowed for thetypeproperty. Something like:In some cases it can be solved by creating a new base type without the properties that differ, but that's not really possible if the type isn't defined in your own code.
yortus commentedon Mar 8, 2016
I wonder if 'outersections' could also be useful in making type guards more general (i.e. not just always starting from a union type and removing things from it). E.g.:
silviogutierrez commentedon Mar 8, 2016
I posted #7423 and was linked to here. Indeed this is very common. Particularly for decorators or component's like
Providerfrom redux.RyanCavanaugh commentedon Mar 15, 2016
This would be a huge amount of work (basically the same amount as intersection or union types were) for a much smaller set of scenarios, but it's still something we want to address at some point. Likely if/when we have a general notion of type operators, this would be the sort of thing that they would handle.
If you run into other scenarios where this would be useful, please do leave a comment so we can prioritize accordingly.
jkillian commentedon Apr 6, 2016
I came across this when working with React also. In my situation, I needed to do something like this:
Since
BandChave conflicting types forfield, they can't both be extended byA.I understand that this is somewhat an untypical scenario and would be a lot of work on the tsc end, but an "outersection" or "subtraction" type would be neat!
raveclassic commentedon Jan 18, 2017
Seems like basic type outersection is already partially implemented when destructuring objects with spreads:
Can this logic be reused for deconstructuring types?
Something like:
UPDATE Have found #10727 and #12215
olyis commentedon Jun 5, 2017
Another use case for this (in particular, permitting subtraction of literals) would be to enable the following:
which could be useful for typing a reasonably large class of objects.