请考虑以下示例。 fetchItems
函数根据传递的onlyBody
参数(默认为true
)返回响应或响应主体。
interface HttpResponse<T> {
body: T
}
function fetchItems<T, B extends boolean>(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, false>('url', false) // Promise<HttpResponse<string>>
const b = fetchItems<string, true>('url', true) // Promise<string>
const c = fetchItems<string, true>('url') // Promise<string>
我想放弃传递B
类型的要求,因为它相对于onlyBody
参数是多余的。但是,如果未明确传递B
类型,则ts编译器会抱怨它(期望2个类型参数,但得到1个)。
const e = fetchItems<string>('url', false); // would want Promise<HttpResponse<string>>
const f = fetchItems<string>('url', true) // would want Promise<string>
const g = fetchItems<string>('url') // would want Promise<string>
我试图将功能签名更改为:
function fetchItems<T, B extends boolean = true>(url: string, onlyBody: B = true as B) {
但是在e
示例中有一个错误:Argument of type 'false' is not assignable to parameter of type 'true | undefined'
是否有任何方法可以更改函数签名,以使e,f,g示例与a,b,c相同? 演示:https://stackblitz.com/edit/typescript-ydkmzk
答案 0 :(得分:2)
函数重载可以满足您的需求:
function fetchItems<T>(url: string, onlyBody: false): Promise<HttpResponse<T>>
function fetchItems<T>(url: string, onlyBody?: true): Promise<T>
function fetchItems<T>(url: string, onlyBody: boolean = true) {
return Promise
.resolve({body: 'some data'} as any)
.then(res => onlyBody ? res.body : res);
}
由于here中所述的TypeScript“设计限制”,具有条件类型的解决方案不起作用。
答案 1 :(得分:1)
您面临的主要问题是TypeScript不支持partial type parameter inference。您必须手动指定所有类型参数(具有默认值的参数除外),或者让编译器推断所有类型参数,但是您不能指定某些参数而让编译器推断其余参数。
对于诸如boolean
之类的类型且具有少量可能的值,如@Nenad's answer所示,使用重载代替通用类型参数是一种解决方法。注释中提到的带有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>>
通过“虚拟”解决方法,编译器可以推断所有参数类型,甚至包括您要手动指定的参数类型。它通过使函数采用您通常会手动指定的类型的伪参数来实现此目的;该函数将忽略虚拟参数:
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"
并没有多大意义,但是对于更复杂的类型,使用该类型变得更加方便断言而不是尝试创建将被扔掉的真实实例。
无论如何,希望其中之一对您有用。祝你好运!