您面临的主要问题是 TypeScript 不支持 partial type parameter inference。要么您必须手动指定所有类型参数(具有默认值的参数除外),要么让编译器推断所有类型参数,但您不能指定一些并让编译器推断其余部分。
使用重载而不是泛型类型参数,如@Nenad's answer 所示,对于像boolean 这样具有少量可能值的类型是一种解决方法。 cmets 中提到的带有boolean 参数(而不是true 或false 一个)的问题可以通过添加另一个重载来解决,如下所示:
function fetchItems<T>(
url: string,
onlyBody: false
): Promise<HttpResponse<T>>;
function fetchItems<T>(url: string, onlyBody?: true): Promise<T>;
// add this overload
function fetchItems<T>(
url: string,
onlyBody: boolean
): Promise<T | HttpResponse<T>>;
function fetchItems<T>(url: string, onlyBody: boolean = true) {
return Promise.resolve({ body: "some data" } as any).then(
res => (onlyBody ? res.body : res)
);
}
const a = fetchItems<string>("url", false); // Promise<HttpResponse<string>>
const b = fetchItems<string>("url", true); // Promise<string>
const c = fetchItems<string>("url"); // Promise<string>
const d = fetchItems<string>("url", Math.random() < 0.5);
// Promise<string|HttpResponse<string>>
我知道另外两种解决方法,我一直称之为 Currying 和 Dummying:
“Currying”解决方法将两个类型参数的单个泛型函数拆分为两个 curried functions,每个都具有一个类型参数。一个你指定,另一个你推断。像这样:
const fetchItems = <T>() => <B extends boolean = true>(
url: string,
onlyBody: B = true as B
) => {
return Promise.resolve({ body: "some data" } as any).then<
B extends true ? T : HttpResponse<T>
>(res => (onlyBody ? res.body : res));
};
你这样称呼它:
const a = fetchItems<string>()("url", false); // Promise<HttpResponse<string>>
const b = fetchItems<string>()("url", true); // Promise<string>
const c = fetchItems<string>()("url"); // Promise<string>
const d = fetchItems<string>()("url", Math.random() < 0.5);
// Promise<string|HttpResponse<string>>
或者,由于所有这些都使用fetchItems<string>(),您可以将其保存到自己的函数中并使用它,以减少冗余:
const fetchItemsString = fetchItems<string>();
const e = fetchItemsString("url", false); // Promise<HttpResponse<string>>
const f = fetchItemsString("url", true); // Promise<string>
const g = fetchItemsString("url"); // Promise<string>
const h = fetchItemsString("url", Math.random() < 0.5);
// Promise<string|HttpResponse<string>>
“Dummying”解决方法让编译器可以推断所有参数类型,甚至是您想要手动指定的参数类型。它通过让函数采用您通常手动指定的类型的虚拟参数来做到这一点;该函数忽略了虚拟参数:
function fetchItems<T, B extends boolean = true>(
dummyT: T,
url: string,
onlyBody: B = true as B
) {
return Promise.resolve({ body: "some data" } as any).then<
B extends true ? T : HttpResponse<T>
>(res => (onlyBody ? res.body : res));
}
const a = fetchItems("dummy", "url", false); // Promise<HttpResponse<string>>
const b = fetchItems("dummy", "url", true); // Promise<string>
const c = fetchItems("dummy", "url"); // Promise<string>
const d = fetchItems("dummy", "url", Math.random() < 0.5);
// Promise<string|HttpResponse<string>>
由于虚拟值只是为了编译器的利益而在运行时未使用,因此您也可以使用 type assertion 来假装您拥有该类型的实例,而不是费力地创建一个实例:
const dummy = null! as string; // null at runtime, string at compile time
const e = fetchItems(dummy, "url", false); // Promise<HttpResponse<string>>
const f = fetchItems(dummy, "url", true); // Promise<string>
const g = fetchItems(dummy, "url"); // Promise<string>
const h = fetchItems(dummy, "url", Math.random() < 0.5);
// Promise<string|HttpResponse<string>>
当然,获得string 值很容易,因此使用null! as string 代替"randomString" 没有多大意义,但是对于更复杂的类型,使用类型断言而不是尝试创建更方便一个真实的例子,你会扔掉的。
无论如何,希望其中一个对你有用。祝你好运!
Link to code