我正在使用帶有 Typescript 的 Directus API。使用 Typescript,它的API呼叫函式會回傳部分物體(例如PartialItem<Book>),因此在繼續傳遞資料之前,我會努力檢查所需屬性的存在。
但是,我仍然收到與 Directus 無關的 Typescript 型別錯誤,但可能與我處理部分型別的方式有關。
這是純 Typescript 的簡化示例(在 TS Playground 上):
interface Book {
id: string;
title?: string;
author?: string;
pages?: number;
}
class Library {
static books: Book[] = [];
static addBook(data: Partial<Book> & {id: string}) {
this.books.push(data);
}
}
function fetchBooks(): Promise<Partial<Book>[]> {
return new Promise((resolve)=>{
setTimeout(()=>{
resolve([
{id: "1234",title:"Nineteen Eighty-Four"},
{id: "2345", title: "The Great Gatsby", author: "F. Scott Fitzgerald"}
])
},1000)
})
}
function bookLoadingFunction() {
fetchBooks().then(books=>{
books.map(b=>{
if(!b.id){
// Handle the fact that the API is missing ID
}else{
Library.addBook(b); // * COMPILE ERROR HERE *
}
})
})
}
即使在檢查之后,編譯器似乎也無法推斷出b.id已定義。我收到以下錯誤:
Argument of type 'Partial<Book>' is not assignable to parameter of type 'Partial<Book> & { id: string; }'.
Type 'Partial<Book>' is not assignable to type '{ id: string; }'.
Types of property 'id' are incompatible.
Type 'string | undefined' is not assignable to type 'string'.
Type 'undefined' is not assignable to type 'string'.
b.id這是編譯器的限制,還是存在確實可能仍然存在的邊緣情況undefined?有沒有辦法讓編譯器滿意而不會失去型別安全?
我知道以下內容會使錯誤消失,但這遠非理想:
Library.addBook(b as Partial<Book> & {id: string});
謝謝。
uj5u.com熱心網友回復:
您期望通過檢查物件的id屬性的真實性,您會得到編譯器將物件從to縮小。不幸的是,這不是 TypeScript 中窄化的作業方式。請參閱microsoft/TypeScript#16976了解(長期)開放功能請求以支持此類事情。Partial<Book>Partial<Book>Partial<Book> & {id: string}
目前,如果您檢查類似 的屬性的值b.id,它只會縮小屬性b.id本身的型別,而不是包含物件的型別b......好吧,除非b是可區分的聯合型別并且id是其區分屬性。但Partial<Book>根本不是一個工會,更不用說一個有歧視的工會了。那好吧。
以下是我能想到的解決方法。一種是通過物件擴展之類的方法,從縮小的屬性和物件的其余部分重新組合您的物件:
if (!b.id) { } else {
const obj = { ...b, id: b.id };
/* const obj: {
id: string;
title?: string | undefined;
author?: string | undefined;
pages?: number | undefined;
} */
Library.addBook({ ...b, id: b.id }); // okay
}
您可以看到它obj的型別等同于Partial<Book> & {id: string}(aka Book)。因此,您可以毫無錯誤地呼叫Library.addBook(obj)。因此,您已經放棄了 narrowing b,而是構建一個b已經變窄的型別的新版本。
如果您不想創建一個基本上等同于舊物件的新物件,您可以放棄檢查if (!b.id) {},而是撰寫一個用戶定義的型別函式,該函式將b作為輸入并回傳一個型別謂詞,表示b可以縮小范圍取決于結果是true還是false。例如:
function hasId<T extends { id?: any }>(
x: T
): x is T & Required<Pick<T, "id">> {
return x.id !== undefined
}
該hasId()函式接受一個x已知具有id屬性(或至少是可選屬性)的引數,并回傳型別 predicate x is T & Required<Pick<T, "id">>。您可以看到它的實際效果:
if (!hasId(b)) { } else {
b // b: Partial<Book> & Required<Pick<Partial<Book>, "id">>
Library.addBook(b); // okay
}
else子句中,has hasId(b)returned true,意思b是縮小為Partial<Book> & Required<Pick<Partial<Book>, "id">>。這也等價于Book(型別Required<Pick<Partial<Book>, "id">>很難看,用Required<T>,Pick<T, K>和Partial<T>實用程式型別撰寫,但如果你仔細閱讀它,你會發現它等價于{id: string})。
所以在這里你決定告訴編譯器如何縮小b,因為它自己不知道如何去做。
Playground 代碼鏈接
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/414487.html
標籤:
下一篇:登錄后無限請求
