Reputation: 2395
According to documentation, I can use predefined Exclude type to exclude certain properties from certain type:
type Test = string | number | (() => void);
type T02 = Exclude<Test, Function>;
However if instead of Test type I'd have Test interface, it doesn't seems to work. How can I achieve similiar result in below case?
interface Test {
a: string;
b: number;
c: () => void;
}
// get somehow interface with excluded function properties??
Upvotes: 5
Views: 2781
Reputation: 328473
To get this effect you need to figure out which properties match Function
, and then selectively exclude them. Equivalently we find which properties do not match Function
, and then selectively include them. This is more involved than just excluding some pieces of a union; it involves mapped types as well as conditional types.
One way to do this is as follows:
type ExcludeMatchingProperties<T, V> = Pick<
T,
{ [K in keyof T]-?: T[K] extends V ? never : K }[keyof T]
>;
type T02 = ExcludeMatchingProperties<Test, Function>;
// type T02 = {
// a: string;
// b: number;
// }
Examining ExcludeMatchingProperties<T, V>
, we can note that the type { [K in keyof T]-?: T[K] extends V ? never : K }[keyof T]
will return the keys in T
whose properties are not assignable to V
.
If T
is Test
and V
is Function
, this becomes something like
{
a: string extends Function ? never : "a";
b: number extends Function ? never : "b";
c: ()=>void extends Function ? never : "c"
}["a"|"b"|"c"]
, which becomes
{ a: "a"; b: "b"; c: never }["a"|"b"|"c"]
, which becomes
{ a: "a"; b: "b"; c: never }["a"] |
{ a: "a"; b: "b"; c: never }["b"] |
{ a: "a"; b: "b"; c: never }["c"]
, or
"a" | "b" | never
, or
"a" | "b"
Once we have those keys, we Pick
their properties them from T
(using the Pick<T, K>
utility type):
Pick<Test, "a" | "b">
which becomes the desired type
{
a: string,
b: number
}
Okay, hope that helps; good luck!
Upvotes: 16