考虑以下示例。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);
}
Run Code Online (Sandbox Code Playgroud)
如果传递了两种泛型类型,则该函数将按预期工作
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>
Run Code Online (Sandbox Code Playgroud)
我想放弃传递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>
Run Code Online (Sandbox Code Playgroud)
我尝试将函数签名更改为:
function fetchItems<T, B extends boolean = true>(url: string, onlyBody: B = true as B) {
Run Code Online (Sandbox Code Playgroud)
但示例中出现错误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
您面临的主要问题是 TypeScript 不支持部分类型参数推断。您必须手动指定所有类型参数(具有默认值的类型参数除外),或者让编译器推断所有类型参数,但不能指定某些类型参数并让编译器推断其余类型参数。
使用重载而不是泛型类型参数,如@Nenad 的答案所示,对于boolean具有少量可能值的类型来说是解决此问题的一种方法。注释中提到的带有boolean参数(而不是 atrue或falseone)的问题可以通过添加另一个重载来解决,如下所示:
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>>
Run Code Online (Sandbox Code Playgroud)
我知道另外两种解决方法,我称之为 Currying 和 Dummying:
“柯里化”解决方法将两个类型参数的单个泛型函数拆分为两个各一个类型参数的柯里化函数。一个是你指定的,另一个是你推断的。像这样:
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));
};
Run Code Online (Sandbox Code Playgroud)
你这样称呼它:
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>>
Run Code Online (Sandbox Code Playgroud)
或者,由于所有这些都使用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>>
Run Code Online (Sandbox Code Playgroud)
“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>>
Run Code Online (Sandbox Code Playgroud)
由于虚拟值只是为了编译器的利益而在运行时不使用,因此您还可以使用类型断言来假装您拥有该类型的实例,而不用费尽心思去创建一个实例:
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>>
Run Code Online (Sandbox Code Playgroud)
当然,获取值非常容易,因此使用代替string没有多大意义,但对于更复杂的类型,使用类型断言会更方便,而不是尝试创建一个将被丢弃的真实实例。null! as string"randomString"
不管怎样,希望其中之一对你有用。祝你好运!
| 归档时间: |
|
| 查看次数: |
954 次 |
| 最近记录: |