Generisk TypeScript med valfri extends
Hur man får den generiska typen ApiResBook att fungera med valfria egenskaper genom att använda extends för inmatningskontroll?
Sandlåda.
Jag har dessa huvudsakliga typer, samma som databasanfält:
// 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
}
För api-hämtningssvar behöver jag en generisk typ som kommer att forma objektet baserat på de begärda fälten
// 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
}
}
// fetch example of usage
async function fn() {
const fetchAPI = <ExpectedData = any>(
apiAction: string,
body: any
): Promise<{ data: ExpectedData } | { error: true }> => {
return new Promise((resolve) => {
fetch(`api`, body)
.then((raw) => raw.json())
.then((parsed: { data: ExpectedData } | { error: true }) => resolve(parsed))
.catch((err) => {
console.log(err)
})
})
}
// response type is { error: true } | {data: { book: { id: string } } }
const response = await fetchAPI<ApiResBook<'id'>>('smth', {})
}
Problemet är med den generiska typen ApiResBook, jag vet inte hur man gör vissa generiska typer valfria. Testexempel inkluderade:
//tests
type BookOnly = ApiResBook<'id'>
type BookWithAuthor = ApiResBook<'id', 'name'>
// should be ok
const bookOnly: BookOnly = { book: { id: '1' } }
const bookWithAuthor: BookWithAuthor = { book: { id: '1', author: { name: 'Max' } } }
// should be error
type BookOnly2 = ApiResBook<'propFoesntExist'>
const bookOnlyError: BookOnly = { book: { id: '1', author: {name: 'Max'} } }
const bookWithoutAuthorError: BookWithAuthor = {book: {id: '1'}}
Svar #1
Löst detta själv :/ Verkar fungera bra.
type ApiResBook<
PickedBookFields extends BookFields,
PickedAuthorFields extends AuthorFields | undefined = undefined,
> = {
book: Pick<Book, PickedBookFields> & {
author: PickedAuthorFields extends AuthorFields ? Pick<Author, PickedAuthorFields> : undefined
}
}
Sandlåda.
