簡體   English   中英

在TypeScript中,是否可以從字符串的輸入類型推斷出區分聯合的字符串文字類型?

[英]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相同的fooCreatorbarCreator對象,並且您可以精確指定有效負載類型和類型字符串。 這里的尷尬之處在於額外的鏈接函數調用。


另一個解決方法是使用偽有效負載參數,該參數允許編譯器推斷TP dummy參數未由defineEvent()函數的主體使用; 它僅由類型系統使用。 這也意味着您實際上並不需要傳遞類型P的實際值。 您可以使用類型斷言來傳遞類似nullundefined

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");

這又是您擁有的fooCreatorbarCreator 當然,這里的笨拙使用的是虛擬參數。 我傾向於使用偽裝而不是偽造,但這取決於您。


好的,希望能有所幫助; 祝好運!

鏈接到代碼

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM