How to create Partial-like, for which you want to set one property

We have a structure that looks like this:

export type LinkRestSource = { model: string; rel?: string; title?: string; } | { model?: string; rel: string; title?: string; } | { model?: string; rel?: string; title: string; }; 

It's almost the same as saying

 type LinkRestSource = Partial<{model: string, rel: string, title: string}> 

Except that this allows you to pass an empty object, while the initial type requires one of the properties that must be passed to

How to create a generic type like Partial , but which behaves like my structure above?

+13
source share
4 answers

I think I have a solution for you. You are looking for something that takes a type T and produces a related type that contains at least one property from T That is, it is similar to Partial<T> , but excludes an empty object.

If so, here it is:

 type AtLeastOne<T, U = {[K in keyof T]: Pick<T, K> }> = Partial<T> & U[keyof U] 

Dissect: First of all, AtLeastOne<T> intersects with something Partial<T> . U[keyof U] means that it is the union of all property values ​​of U And I defined (default) U as a mapped type , where each T property is mapped to Pick<T, K> , a type with one property for the K key, (For example, Pick<{foo: string, bar: number},'foo'> equivalent to {foo: string} ... it "selects" the 'foo' property from the original type.) This means that U[keyof U] in this case is the union of all possible types with one property from T

Hmm, this can be confusing. Let's see step by step how it works with the following specific type:

 type FullLinkRestSource = { model: string; rel: string; title: string; } type LinkRestSource = AtLeastOne<FullLinkRestSource> 

It expands to

 type LinkRestSource = AtLeastOne<FullLinkRestSource, { [K in keyof FullLinkRestSource]: Pick<FullLinkRestSource, K> }> 

or

 type LinkRestSource = AtLeastOne<FullLinkRestSource, { model: Pick<FullLinkRestSource, 'model'>, rel: Pick<FullLinkRestSource, 'rel'>, title: Pick<FullLinkRestSource, 'title'> }> 

or

 type LinkRestSource = AtLeastOne<FullLinkRestSource, { model: {model: string}, rel: {rel: string}, title: {title: string}> }> 

or

 type LinkRestSource = Partial<FullLinkRestSource> & { model: {model: string}, rel: {rel: string}, title: {title: string}> }[keyof { model: {model: string}, rel: {rel: string}, title: {title: string}> }] 

or

 type LinkRestSource = Partial<FullLinkRestSource> & { model: {model: string}, rel: {rel: string}, title: {title: string}> }['model' | 'rel' | 'title'] 

or

 type LinkRestSource = Partial<FullLinkRestSource> & ({model: string} | {rel: string} | {title: string}) 

or

 type LinkRestSource = {model?: string, rel?: string, title?: string} & ({model: string} | {rel: string} | {title: string}) 

or

 type LinkRestSource = { model: string, rel?: string, title?: string } | {model?: string, rel: string, title?: string} | {model?: string, rel?: string, title: string} 

which, I think, is what you want.

You can check this out:

 const okay0: LinkRestSource = { model: 'a', rel: 'b', title: 'c' } const okay1: LinkRestSource = { model: 'a', rel: 'b' } const okay2: LinkRestSource = { model: 'a' } const okay3: LinkRestSource = { rel: 'b' } const okay4: LinkRestSource = { title: 'c' } const error0: LinkRestSource = {} // missing property const error1: LinkRestSource = { model: 'a', titel: 'c' } // excess property on string literal 

So, does this work for you? Good luck

+22
source

This is not possible with Partial<T> . Under the hood, it looks like this:

 type Partial<T> = { [P in keyof T]?: T[P]; }; 

All properties are optional.

I doubt it is possible (or easy) to enforce your rule through a type system.

You can try to create a type that uses keyof similar way, but has a condition in the default constructor.

If you can come up with a way to declare a type of type Partial that builds a matrix of types of type yours emitting ? for different keys in each and concat all of them using | as in your first example, maybe you can apply your rule system like vie.

This keyword blog post may give you some ideas.

+2
source

There is another solution if you know what properties you want.

 AtLeast<T, K extends keyof T> = Partial<T> & Pick<T, K> 

It will also allow you to lock several keys of the type, for example, AtLeast<T, 'model' | 'rel'> AtLeast<T, 'model' | 'rel'> .

+1
source

Maybe something like this:

 type X<A, B, C> = (A & Partial<B> & Partial<C>) | (Partial<A> & B & Partial<C>) | (Partial<A> & Partial<B> & C); type LinkRestSource = X<{ model: string }, { rel: string }, { title: string }> var d: LinkRestSource = {rel: 'sdf'}; 

But it's a little dirty :)

or

 type Y<A, B, C> = Partial<A & B & C> & (A | B | C); 
0
source

Source: https://habr.com/ru/post/1259512/


All Articles