網站搜尋

TypeScript:條件型別不適用於可選參數


我有一個資料來源,我們稱它為 getData(),它會回傳物件。有時它回傳已知類型的物件(例如 Person、Animal),但有時回傳的物件形狀是未知的。

(TypeScript 遊樂場連結)

type Person = { name: string; age: number };
type Animal = { species: string };

/** 
 * This interface stores all known object types returned by `getData()`.
 * I'm storing it as an interface instead of `Person | Animal` because I
 * need to store the "code" of a known type (e.g. `"person"` for `Person` type).
 */
interface DataCategory {
  person: Person;
  animal: Animal;
}

/** Our data source */
const getData: Person | Animal | any = () => {
  return {};  // Mocked value
};

現在我希望編寫一個輔助函數 useData() 來縮小 getData() 的返回值。它接受一個可選的參數,類型為 keyof DataCategory,並返回對應的類型。我希望當不傳入參數時,這個函數 返回 any。

const person = useData("person");  // const person: Person
const animal = useData("animal");  // const animal: Animal
const notKnown = useData();   // const notKnown: any

我已經嘗試了以下實作方法:

function useData<T extends keyof DataCategory>(category?: T) {
  const data: any = getData();
  return data as T extends undefined ? any : DataCategory[T];
}

const animal = useData("animal");
//    ^ const animal: Animal

const notKnown = useData();
//    ^ const notKnown: Person | Animal
// However, I want the above to be `const notKnown: any`

那行不通,因為 useData() 返回的是 Person | Animal 而不是 any。我該如何解決這個問題?

答案#1

預設情況下,TS 會使用約束作為它無法推斷的型別參數的型別(在這種情況下,沒有地方可以從中推斷 T)。你可以使用 any 作為預設值來改變這一點:

function useData<T extends keyof DataCategory = any>(category?: T) {
  const data: any = getData();
  return data as T extends undefined ? any : DataCategory[T];
}

遊樂場連結

正如 Michael Rose 在他的回答中指出的,你也可以使用重載,這對未來的讀者來說可能更容易理解。

答案#2

嘗試使用泛型的另一種方法是直接重載你的 useData 函數並指定類別:

function useData(category: "animal"): Animal;
function useData(category: "person"): Person;
function useData(): any;
function useData(category?: string): any {
  return getData();
}

const animal = useData("animal");
//    ^ const animal: Animal

const notKnown = useData();
//    ^ const notKnown: any