ZiiMakc
ZiiMakc

Reputation: 36876

Typescript Pick Array not working as expected

I'm not sure why there is a problem if i use this line as array and how to fix it:

Pick<Author, PickedAuthorFields>[]

Sandbox.

If i use never instead of conditional type, than i have another problem with required author field.

Sanbox 2

Sandbox 3 with optional ? author had another problems...

Sanbox 3

This ApiResBook generic type is needed for many pages that will make requests to api with different expected output fields based on request.

Maybe there is alternative approach, i can change shape of object if needed.

// Main types as in database - should't be changed
type Book = {
  id: string
  title: string
  visible: boolean
  author: string
}

type Author = {
  id: string
  name: string
}

// Inhereted from types from main
type BookFields = keyof Book
type AuthorFields = keyof Author

// type for generating expected fetch response from API
type ApiResBook<
  PickedBookFields extends BookFields,
  PickedAuthorFields extends AuthorFields | undefined = undefined,
> = {
  book: Pick<Book, PickedBookFields> & {
    author: PickedAuthorFields extends AuthorFields ? Pick<Author, PickedAuthorFields>[] : undefined
  }
}

// tests
type BookWithAuthor = ApiResBook<'id', 'name' | 'id'>

// should be ok
const bookWithAuthor1: BookWithAuthor = { book: { id: '1', author: [{ id: '1' }] } }
const bookWithAuthor2: BookWithAuthor = { book: { id: '1', author: [{ name: 'Max' }] } }
const bookWithAuthor3: BookWithAuthor = { book: { id: '1', author: [{ name: 'Max', id: '1' }] } } // why error?

Upvotes: 3

Views: 5900

Answers (1)

Maciej Sikora
Maciej Sikora

Reputation: 20152

It was a while to figurate out but I think this is the solution:

type Book = {
  id: string
  title: string
  visible: boolean
  author: string
}

type Author = {
  id: string
  name: string
}

// Inhereted from types from main
type BookFields = keyof Book
type AuthorFields = keyof Author

// type for generating expected fetch response from API
type ApiResBook<
  PickedBookFields extends BookFields,
  PickedAuthorFields extends AuthorFields | null = null,
  AuthorObj = {author: Pick<Author, Exclude<PickedAuthorFields, null>>[]}
> = {
    book: Pick<Book, PickedBookFields> & (PickedAuthorFields extends null ? {} : AuthorObj)
}


// tests
type BookWithAuthor = ApiResBook<'id', 'name' | 'id'>
type BookWithoutAuthor = ApiResBook<'id'>

// should be ok
const bookWithAuthor0: BookWithoutAuthor = { book: { id: '1' } }
const bookWithAuthor3: BookWithAuthor = { book: { id: '1', author: [{ id: '1', name: 'Max' }] } }

PS. null can be replaced by undefined or any other unit type

Upvotes: 3

Related Questions