[英]Different object fields in Typescript based on conditional type
I want to write a function (eventual use is a React function component) in Typescript that takes a props object with a list of list of objects of any type. 假設 function 應該為每個打印一個“密鑰”,其中如果 object 類型有一個 id 字段,那么 id 值將打印為鍵,如果 ZA8CFDE6331BD59EB2AC96F 類型不是從 8911C4B666 派生的字段,則道具 object 中的訪問器 function (此處為偽代碼,省略類型):
function process(props: { items: ..., ...}) {
props.items.forEach(item => {
if (item.id) {
console.log(`Key for ${item.id}`)
} else {
console.log(`Key for ${props.keyFunction(item)}`)
}
})
}
process({items: [{id: "1", name: "A"}, {id: "2", name: "B"}]})
process({items: ["A", "B"], keyFunction: (item) => item})
理想情況下,我想要以下內容:
keyFunction
如果提供了 keyFunction 但項目已經有 id 應該會出錯keyFunction
應該出錯process
內適當位置的 keyFunction 和 id function 主體(自動完成應該可以工作)有沒有辦法為此 function 編寫類型,以便上述所有 3 個工作?
注意:我知道如果這些是參數而不是配置 object 的值,我可以使用有條件的 function 重載,但是因為實際用例是 React ZC1C425268E68385D14AB5074C17Z 組件,所以這里的組件不會'4
我嘗試使用在調用點有效的條件類型,但我不知道如何讓keyFunction
正確了解 keyFunction 游樂場鏈接:
type KeyProps<T> = T extends { id: string }
? { items: T[] }
: {
items: T[];
keyFunction(item: T): string;
};
function process<T>(props: KeyProps<T>) {
props.items.map(item => {
if (item.id) {
console.log(`Key for ${item.id}`)
} else {
console.log(`Key for ${props.keyFunction(item)}`)
}
})
}
我也嘗試過使用有區別的聯合,但我不知道如何為具有泛型的聯合的一個分支提供類型約束:
type KeyProps<T> =
| { type: "autokey", items: T[] } // How to provide that T should extend { id: string } here?
| { type: "normal", items: T[], keyFunction(item: T): string }
注意:此答案假定這兩個參數包含在包裝器道具 object 內並不重要。
理想情況下,我想要以下內容:
keyFunction
如果提供了 keyFunction 但項目已經有 id 應該會出錯- 如果項目沒有 id 且未提供 keyFunction,
keyFunction
應該出錯- Typescript 應該知道
process
內適當位置的 keyFunction 和 id function 主體(自動完成應該可以工作)
好的,因此只需使用 function 過載即可解決要求二 (2)。 可以通過將實現邏輯移動到它們自己的類型安全函數中來解決要求三 (3)。 但是,要求一 (1) 非常棘手。 我們可以使用泛型T
輕松推斷items
的類型。 然后使用T
我們可以派生一個類型D
,使得D
不包含任何具有id
鍵的 object 類型。 棘手的部分是設法從items
派生基本類型T
,同時將items
的類型限制為D
。 我這樣做的方法是使用交叉類型T[] & D[]
作為items
的類型。 這將從items
數組中的元素推斷出基本類型T
,同時也限制items
數組中元素的類型。
interface ObjWithID {
[k: string]: any;
id: string;
}
type AnyWithoutID<T> = T extends { id: any } ? never : T;
function processItemsWithID(items: ObjWithID[]) {
items.forEach(item => console.log(`Key for ${item.id}`))
}
function processItemsWithoutID<T, D extends AnyWithoutID<T>>(items: T[] & D[], keyFunction: (value: T) => string) {
items.forEach(item => console.log(`Key for ${keyFunction?.(item)}`))
}
function process(items: ObjWithID[]): void;
function process<T, D extends AnyWithoutID<T>>(items: T[] & D[], keyFunction: (value: T) => string): void;
function process(items: any[], keyFunction?: (value: any) => any) {
if (keyFunction) {
return processItemsWithoutID(items, keyFunction);
} else {
return processItemsWithID(items);
}
}
process([{id: "1", name: "A"}, {id: "2", name: "B"}])
process([{id: "1", name: "A"}, {id: "2", name: "B"}], (item) => item)
process([{ name: "A"}, { name: "B"}], (item) => item.name)
process(["A", "B"], (item) => item)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.