[英]In TypeScript is it possible to infer string literal types for Discriminated Unions from input type of string?
當前在我的TypeScript應用程序中創建事件如下所示:
// This function creates event creators
function defineEvent<E extends {type:string, payload:any}>(type:E["type"]) {
return (payload:E["payload"]) => ({
type,
payload
})
}
// This function creates foo events
const fooCreator = defineEvent<{
type:"foo",
payload: {
foo:string
}
}>("foo");
// This function creates bar events
const barCreator = defineEvent<{
type:"bar",
payload:{
bar:string
}
}>("bar");
// Example events
const fooEvent = fooCreator({foo:'foo'});
const barEvent = barCreator({bar:'bar'});
// Create a union type to switch over
type AppEvent = ReturnType<typeof fooCreator> | ReturnType<typeof barCreator>;
// Example of switching with a discriminated union
function switchOnEvents(event: AppEvent) {
switch(event.type){
case "foo":
// compiler is happy about payload having a foo property
return event.payload.foo.toLowerCase();
case "bar":
// compiler is happy about payload having a bar property
return event.payload.bar.toLowerCase();
}
}
這行之有效,但是這意味着我在定義事件創建者時需要兩次指定事件類型,這在某些方面可能被認為是多余的。
const fooCreator = defineEvent<{
type:"foo", // defining type here
payload: {
foo:string
}
}>("foo"); // also here to create the actual string value
是否可以為創建者函數創建返回類型,該返回類型從輸入type
參數中提取字符串文字到工廠函數?
這意味着如果事件創建者是這樣提供的,則上面的示例仍然可以使用:
const barCreator = defineEvent<{
payload:{
bar:string
}
}>("bar");
我猜我需要合並兩種類型,但以某種方式推斷字符串文字。
但是,這不起作用:
function defineEvent<E extends { payload:any}, ET extends string>(type:ET) {
type RE = E & {type: ET};
return (payload:E["payload"]) => {
return ({
type,
payload
} as any ) as RE
}
}
編譯器要我傳遞第二種類型的參數。
const fooCreator = defineEvent<{
payload: {
foo:string
} //Expected 2 type arguments, but got 1.
}>("foo");
有誰知道該怎么做或是否不可能?
這里的問題是TypeScript尚不支持部分類型參數推斷 。 您的defineEvent()
函數從根本上取決於兩種類型:有效負載類型(稱為P
)和uh“類型”字符串文字類型(稱為T
)。 您想指定P
但是讓編譯器推斷T
,這是不支持的。 您可以指定它們兩者,或者編譯器可以嘗試推斷它們兩者。
因此,我知道有兩種可能的解決方法。 一種是使用currying,其中一個函數返回另一個函數。 第一個函數在P
是通用的,您將指定它,而返回的函數在T
是通用的,這將從該函數的參數推斷出來。 像這樣:
const defEvent = <P>() => <K extends string>(type: K) => (payload: P) => ({
type,
payload
});
const fooCreator = defEvent<{ foo: string }>()("foo");
const barCreator = defEvent<{ bar: string }>()("bar");
這為您提供了與fooCreator
相同的fooCreator
和barCreator
對象,並且您可以精確指定有效負載類型和類型字符串。 這里的尷尬之處在於額外的鏈接函數調用。
另一個解決方法是使用偽有效負載參數,該參數允許編譯器推斷T
和P
dummy參數未由defineEvent()
函數的主體使用; 它僅由類型系統使用。 這也意味着您實際上並不需要傳遞類型P
的實際值。 您可以使用類型斷言來傳遞類似null
或undefined
:
const defEvent = <P, T extends string>(payloadDummy: P, type: T) => (
payload: P
) => ({
type,
payload
});
const fooCreator = defEvent(null! as { foo: string }, "foo");
const barCreator = defEvent(null! as { bar: string }, "bar");
這又是您擁有的fooCreator
和barCreator
。 當然,這里的笨拙使用的是虛擬參數。 我傾向於使用偽裝而不是偽造,但這取決於您。
好的,希望能有所幫助; 祝好運!
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.