Reputation: 163
I'd like to write a typescript function that takes an object that includes the parameter foo
and outputs the same object without the parameter foo
. I want to do this all in a typesafe manner. Seems pretty simple, but I'm stumbling. Here is the code I have so far:
interface FooProp {
foo: string;
}
type WithFooProp<P extends object> = P extends FooProp ? P : never;
type WithoutFooProp<P extends object> = P extends FooProp ? never: P;
function withoutFooProp<P extends object>(input: WithFooProp<P>): WithoutFooProp<P> {
// How do I get rid of these any's
const {foo, ...withoutFoo} = input as any;
return withoutFoo as any;
}
This isn't great because I use any
a bunch. I'd sorta expect the code to work as is without any
, but TS complains that input
isn't an object and can't be destructured. How could I improve this method?
In addition, when I use this function, TS forces me to provide the generic types explicitly. I wish it would infer the types implicitly. Is there any way I could write the function to implicitly grab the parameters.
// Compiles - but I have to specify the props explicitly
withoutFooProp<{foo: string, bar: string}>({
foo: 'hi',
bar: 'hi',
});
// This doesn't compile - I wish it did! How can I make it?
withoutFooProp({
foo: 'hi',
bar: 'hi',
});
Thanks in advance!
Upvotes: 2
Views: 2940
Reputation: 1103
You can also use this typesafe function which works as generic and can be used for any object:
export function deleteProperty<TObject, TProp extends keyof TObject>(object: TObject, prop: TProp): Omit<TObject, TProp> {
const {
[prop]: toDelete,
...newObject
} = object
return newObject;
}
Upvotes: 1
Reputation: 30919
You can let the parameter type be simply P
, where P
is constrained to contain the foo
property, so that type inference will work: it will just set P
to the argument type. Then use Pick
and Exclude
to generate the type of the output object containing all properties of P
except foo
.
interface FooProp {
foo: string;
}
type WithoutFooProp<P> = Pick<P, Exclude<keyof P, "foo">>;
function withoutFooProp<P extends FooProp>(input: P): WithoutFooProp<P> {
const {foo, ...withoutFoo} = input as any;
return withoutFoo;
}
You won't be able to get rid of the any
unless/until this suggestion is implemented. I wouldn't worry about it.
This withoutFooProp
function should work on any concrete input type and give you a concrete output type. If you are using withoutFooProp
on objects of generic types, then you may run into problems because of TypeScript's limited ability to reason about Pick
and Exclude
, and you may wish to use intersections instead (even though doing so is technically unsound), as demonstrated in this answer in the context of React higher-order components.
Upvotes: 3