Issue
I don't know if what I'm trying to do is possible but here's the idea:
Let's assume we have a generic type:
type Foo<T> = {
bar: T
}
Is there any T
type which would let me omit the property completely, without making it optional in the type Foo
(so without ?:
or T | undefined
, etc)?
So this would pass compiler checks (where I'm looking for what _
is)?
const x: Foo<_> = {}
Use case:
I'm trying to create a generic HttpRequest
type, like so:
type HttpRequest<Body, Query, Path, Headers> = {
body: Body
query: Query
path: Path
header: Headers
}
And I'd like to be able to use it with any combination of the fields present, and I don't want to make all fields optional. Meaning I'd like to be able to do this:
const x: HttpRequest<{ test: number }, _, { foo: string }, _> = {
body: { test: 1 },
path: { foo: "bar" },
}
I used _
as the magic type I'm looking for that lets me ommit the field.
Now I know I could do this:
type HasBody<B> = {
body: B
}
type HasPath<P> = {
path: P
}
// ....
And then combine these types:
type CustomRequest = HasBody<{ test: number }> & HasPath<{ foo: string }>
const x: CustomRequest = {
body: { test: 1 },
path: { foo: "bar" },
}
But it would save me a lot of time if the above mentioned type existed.
Solution
You're looking to conditionally omit properties from an object, based on whether or not the property is of some "special" type (which you're calling _
). There's no built-in functionality to do this, but you can build something that works this way:
type _ = never;
type Omit_<T> = { [K in keyof T as T[K] extends _ ? never : K]: T[K] }
You can define _
to be any property type you don't want to support being part of the object. I've chosen the never
type here, but you could use undefined
, or a nonce class
type with a private
member to simulate nominal typing, or anything really that won't interfere with "valid" property types.
Then Omit_<T>
will take a type T
and omit all the properties whose value is of the _
type. It does this using key remapping in mapped types.
You can verify that it works:
type Z = Omit_<{ a: 1, b: 2, c: _, d: 4 }>
/* type Z = {
a: 1;
b: 2;
d: 4;
} */
Armed with that, you can define Foo
type Foo<T> = Omit_<{
bar: T
}>;
const x: Foo<_> = {}
and HttpRequest
type HttpRequest<Body, Query, Path, Headers> = Omit_<{
body: Body
query: Query
path: Path
header: Headers
}>
const y: HttpRequest<{ test: number }, _, { foo: string }, _> = {
body: { test: 1 },
path: { foo: "bar" },
}
Looks good. There are probably caveats here, especially around type inference, so you should fully test against your use cases before adopting something like this.
Answered By - jcalz
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.