如何使用扩展进行输入检查,使通用 ApiResBook 类型与可选道具一起使用?
Sandbox https://www.typescriptlang.org/play/index.html#code/PTAEFkEMEsDtQC4E8AOBTAzqSW6gCaQKQBGOaoAtKBgBYD2ArgDb4DkCoJFAxrZLADmafAFgAUMnSgAQvXoBrUAF5QAbwmhQ0fAC4aCAE5xBmxNATM0+jEZNmAbtAzQSV-SXlWBZyIwQMhjZ2QhIAvhISUhQAgv6BKupmOsHGoeJasJAAttYGaabiEeISIKAAkrC0aIZoCCKgAGaG9NmIqJhNLW3ZMLBRHbLyCgBi0GisWKoKaEj0jUOKA9JxAfSGYxP4U6AzcwurgZHiZdFN66DCsDVEJqBoAB7oPPX4TXV8oLUYKPSwGBRmq1QDEAArlZaxFDQABKmDkigAPGZQdAeDN8AjRuNJvcHvVYNtFtithgADQotEYw7rTa4x4Eok0jY4okAH1AjEJaEacAaqi5+B5fPwFPEAD5EhoMlxhvpUejEViyaAFRisXTtpKAGRJGVaPxrQwAfnlVJEzM1WAZaEJWEtrKwxtVVMRzJVaot8Vpjsl+kFwuuYhlxWKpTAjQ+tDxORQVlA805GEgwgkOCQsB4TS5L2gfyasAAFABKPVmHh-WzvBB8MHlRKIgCiTzQLxEABEiJBEgIkOLC2YDdCYrm-qkTGKtFpPPgkPpe2Zi-Lus40Ii1AQu-pm89Xp3iKAwqAORuai0gohDIwKGFJcpJdKp186oxDPBrgB3VUrgGFwvfehmAcNBS3vPUny0SMa1oQsAANIGhWCVRnJBi0HCCADoAltP9DEgD9QMlPCPwwgArDA-hLND9Uw7Ci0LFBIEMAE9HUTdiG3Fs23wfduyPE97kMc99CMa9D0I58KKAtAGKYlji2oiCpwwngiD4P8zwkx8lKnCt-kAtAMOYehBELTT0KfMJFKnKyzDDGUym+X5-goM5nDYs91hEq8KEPY91EIDi2M8RR9A3FJ8juI9ovLStOCcysKFUfCYE4KDa3BN1oThDAsURNgdDYcV+zYDBsgCNgVTUKzQHCY56pOMB6lsDBIWJAB5WBmCQRIYmy+FhnywrxTarEAHULFoZlev63LBoK9gVTYLJciK44yjoJhWC4CglnEPSqxChROu6-QsROnrVA3I6wu0Vi2AARjYPzigOzgjomgJmTO4ZPqm71DClWVQrYiLHsq7AAdula8jYKAHme6LD3WsBNpYN5uEE89RuGC7GyEi5VD62EBqRNgUBaFB23oTBYAQZtnAQNbJEGcbJuZfHzxmkm5rJwqlop+gqZpjA6YZ2xmbe4Hjq6pBOa8jrZaBm7Qfup6VUNQIwph-Q4cgBGkaPCQpY+yamAQDmCYvNmvoBqUVbUMGnrCI8gA.
我有这样的主要类型,与数据库字段相同:
// 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
}
对于 api 获取响应,我需要通用类型,该类型将根据请求的字段塑造对象
// 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', {})
}
问题在于通用 ApiResBook 类型,我不知道如何使某些通用类型可选。测试示例包括:
//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'}}