[英]TypeScript type discriminated unions in function arguments
[英]Detecting a type from a partial of a discriminated unions in typescript
我在檢測 function 的類型時遇到問題,該類型從可區分的聯合中獲取部分字段並從聯合中返回匹配類型。 我有一個create()
function,它采用不包括時間戳和 ID 的字段,然后我返回帶有 ID 和時間戳的字段。 我們在參數中有type
字段並返回 object 但它不用於確定返回類型。
interface Node {
type: string
id: string
createdAt: number
updatedAt: number
}
interface Pokemon extends Node {
type: 'Pokemon'
name: string;
}
interface Trainer extends Node {
type: 'Trainer'
name: string;
pokemon: string[]
}
type CreateNode<T extends Node> = T extends unknown
? Omit<T, 'id' | 'createdAt' | 'updatedAt'>
: never
function create<R extends Node>(obj: CreateNode<R>): R {
return {
...obj,
id: 'ds',
createdAt: 4,
updatedAt: 5,
} // give as error "... as is assignable to the constraint of type 'R', but 'R' could be instantiated with a different subtype of constraint 'Node'."
}
const pokemon = create<Pokemon|Trainer>({
type: 'Pokemon',
name: 'Garalaxapon'
})
pokemon //should be Pokemon but is Pokemon | Trainer
我不明白退貨中的錯誤,但我確信這是它的症結所在。 謝謝!
我將忽略實現內部的問題,這是與您所詢問的問題不同的問題。 現在我將假設實現工作,並使用declare
語句只關注調用簽名問題。
您面臨的問題是您對單個R
泛型類型期望過高。 您手動指定它是為了讓編譯器知道您想要區分哪些特定的Node
子類型聯合。 但是,您還希望編譯器以某種方式將R
縮小到該工會的成員之一。 這行不通。 一旦您手動將R
指定為聯合,它就是永遠的聯合。
您可以使用兩個通用參數來解決這個問題; 一個( R
)您為區分聯合指定,一個( T
)編譯器從傳入的參數推斷。 不幸的是,您不能在單個 function 簽名中執行此操作; 要么您需要手動指定R
和T
,否則編譯器將嘗試推斷R
和T
。 TypeScript 中沒有部分類型參數推斷 (microsoft/TypeScript#26242) 。
有時在這種情況下,我使用類型參數柯里化將兩個類型參數的單個 function 拆分為多個單類型參數函數。 在您的情況下,它看起來像這樣:
declare function create<R extends Node>(): <T extends CreateNode<R>>(obj: T) => Extract<R, T>;
注意R
是如何對應全可區分聯合的,而T
指的是傳入的obj
參數的類型。 您使用Extract
實用程序類型區分R
和T
:僅返回可分配給T
的R
的元素:
const createPokemonOrTrainer = create<Pokemon | Trainer>();
const pokemon = createPokemonOrTrainer({
type: 'Pokemon',
name: 'Garalaxapon'
}); // Pokemon
這里, create()
返回另一個 function,並create<Pokemon | Trainer>()
create<Pokemon | Trainer>()
返回您之前嘗試制作的 function:接受部分Pokemon | Trainer
Pokemon | Trainer
並將其Pokemon
或Trainer
。
但也許你實際上根本不需要R
; 您是否打算每次都使用不同的歧視聯合來調用create()
? 如果你只打算使用單一的聯合類型,比如Pokemon | Trainer
Pokemon | Trainer
,那么您可以對其進行硬編碼。 本質上,不要打擾過度通用的create()
,只需手動編寫createPokemonOrTrainer()
:
type DiscrimUnion = Pokemon | Trainer;
declare function createDiscrimUnion<T extends CreateNode<DiscrimUnion>>(obj: T): Extract<DiscrimUnion, T>;
const pokemonAlso = createDiscrimUnion({
type: 'Pokemon',
name: 'Garalaxapon'
}); // Pokemon
好的,希望有幫助; 祝你好運!
pokemon //should be Pokemon but is Pokemon | Trainer
這是對的。 原因:
你有const pokemon = create<Pokemon|Trainer>
where create<R extends Node>(obj: CreateNode<R>): R
。 由於create
返回傳入的R
,因此 pokemon 將是您傳入的R
。您正在傳入Pokemon|Trainer
所以pokemon: Pokemon|Trainer
如果這是您想要的,請使用Pokemon
,即create<Pokemon>
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.