[英]Typescript - Assigning different argument to function
我想制作通用 function 以將項目添加到數組中。 我還希望它通過智能等進行類型保護。
我做了這樣的smt:
interface IOne {
id: number;
text: string;
}
interface ITwo {
id: number;
data: string;
}
interface IItems {
one: IOne[];
two: ITwo[]
}
const list: IItems = {
one: [],
two: []
}
function add(type: 'one', data: IOne): void
function add(type: 'two', data: ITwo): void
function add(type: 'one'|'two', data: IOne|ITwo) {
list[type].push(data)
}
但出現錯誤:
'IOne | 類型的參數 ITwo' 不可分配給類型為“IOne & ITwo”的參數。 類型“IOne”不可分配給類型“IOne & ITwo”。 “IOne”類型中缺少屬性“數據”,但在“ITwo”類型中是必需的。
如何處理?
這根本不是 TypeScript 編譯器可以驗證為安全的東西。 每個type
和data
都是聯合類型,但編譯器將無法理解這些聯合是否相關; 它將它們視為獨立的,因此它認為該type
可能是"one"
,而data
是ITwo
,並抱怨這樣做不安全。
microsoft/TypeScript#30581上有一個未解決的問題,要求支持處理此類相關聯合,但它主要是一個倉庫,用於記錄人們何時遇到問題並提到最好的辦法就是使用類型斷言繼續前進。
您編寫的特定重載實現實際上不是類型安全的,因為實現簽名明確表示type
和data
是不相關的聯合:
function add(type: 'one' | 'two', data: IOne | ITwo) {
list[type].push(data); // error!
}
這是一個合理的錯誤; 即使在原則上,編譯器也不知道list[type]
會接受data
。 由於您碰巧從調用簽名中知道這會很好,因此您可以使用類型斷言來抑制錯誤。 例如:
function add(type: 'one' | 'two', data: IOne | ITwo) {
(list[type] as (IOne | ITwo)[]).push(data); // okay
}
在這里,您告訴編譯器list[type]
是一個數組,其元素可以是IOne
或ITwo
。 這在技術上並不正確( list.one
和list.two
應該同時包含IOne
和ITwo
元素),但只要您確定實現是正確的,就相對無害。 這通常是類型斷言的情況; 您承擔了保證類型安全的一些責任,因為編譯器不能。
如果您想實際在實現中表示type
和data
之間的相關性(因此您甚至不需要重載),您可以使用 rest 參數,其類型是元組類型的聯合:
declare function add(...args: [type: "one", data: IOne] | [type: "two", data: ITwo]): void;
您可以驗證這只能以您想要的方式調用:
add("one", { id: 1, text: "" }); // okay
add("two", { id: 2, data: "" }); // okay
add("one", { id: 2, data: "" }); // error
但是由於 microsoft/TypeScript#30581 中提到的限制,編譯器仍然無法在實現中遵循相關性:
function add(...args: [type: "one", data: IOne] | [type: "two", data: ITwo]) {
list[args[0]].push(args[1]); // error!
}
至少在這里,您可以通過編寫冗余代碼來解決它,以強制編譯器通過控制流分析遍歷"one"
和"two"
的不同情況:
function add(...args: [type: "one", data: IOne] | [type: "two", data: ITwo]) {
if (args[0] === "one") {
list[args[0]].push(args[1]); // okay
} else {
list[args[0]].push(args[1]); // okay
}
}
但是這種冗余很煩人,並且不能很好地擴展。 如果讓編譯器驗證類型安全比開發人員的生產力和編寫慣用的 JavaScript 更重要,我只會建議這樣的事情。 相反,即使在這種情況下,最合理的解決方案就是使用類型斷言:
function add(...[type, data]: [type: "one", data: IOne] | [type: "two", data: ITwo]) {
(list[type] as (IOne | ITwo)[]).push(data); // okay
}
這會讓你回到非常類似於重載版本的東西。 那好吧!
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.