Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/typescript/9.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Typescript 基于泛型参数返回函数签名_Typescript_Typescript Typings_Typescript2.0_Typescript1.8_Typescript Generics - Fatal编程技术网

Typescript 基于泛型参数返回函数签名

Typescript 基于泛型参数返回函数签名,typescript,typescript-typings,typescript2.0,typescript1.8,typescript-generics,Typescript,Typescript Typings,Typescript2.0,Typescript1.8,Typescript Generics,我有一个函数createRequest: 函数createRequest(方法:string,路径:string){ 返回函数resourceApiCall(){ //…附加逻辑 返回httpCall(路径、方法) } } 返回一个我想调用的函数resourceApiCall,如: const fetchUsers=createRequest('GET','/users')) 等待fetchUsers({createdAfter:new Date()}) 我还想做一些类似的事情: const

我有一个函数
createRequest

函数createRequest(方法:string,路径:string){
返回函数resourceApiCall(){
//…附加逻辑
返回httpCall(路径、方法)
}
}
返回一个我想调用的函数
resourceApiCall
,如:

const fetchUsers=createRequest('GET','/users'))
等待fetchUsers({createdAfter:new Date()})
我还想做一些类似的事情:

const fetchPayment=createRequest('GET','/payments'))
等待fetchPayment('id',{createdAfter:new Date()})
我的问题是,如何将定义传递给
createRequest
,以便
fetchUsers
fetchPayment
在IDE中显示正确的函数参数和返回值(任何类型的检查都正确)

我相信我需要做一些事情,比如:

支付接口{
(id:string,{createdAfter:Date}):承诺
}
const fetchPayment=createRequest('GET','/payments')
但理想情况下,我想做如下工作:

const fetchPayment=createRequest('GET','/payments'))
函数createRequest(方法:string,路径:string){
返回函数resourceApiCall(…args:args){
//…附加逻辑
返回httpCall(路径、方法)
}
}

您可以将别名和重载组合在一起以使其正常工作。基本上,将这些参数别名为字符串文字类型,然后为函数提供多个签名。然后,TypeScript可以根据传入的参数推断
createRequest
的返回类型

type UserPath = '/users';
type PaymentPath = '/payment';
type CreatedAfter = {
  createdAfter: Date;
};

function createRequest(
  HttpVerb: string,
  target: UserPath
): (id: string, date: CreatedAfter) => Promise<{ id: string }>;

function createRequest(
  HttpVerb: string,
  target: PaymentPath
  //I'm just guessing the return type here
): (date: CreatedAfter) => Promise<{ id: string }[]>; 

function createRequest(HttpVerb: string, target: UserPath | PaymentPath): any {
  //your function implementation doesn't have to be like this, this is just so
  //this example is fully working
  if (target === '/users') {
    return async function(date) {
      return { id: '1' };
    };
  } else if (target === '/payment') {
    return async function(id, date) {
      return [{ id: '1' }];
    };
  }
}

//this signature matches your fetchUsers signature
const fetchUsers = createRequest('GET', '/users'); 

//this signature matches your fetchPayment signature
const fetchPayment = createRequest('GET', '/payment');
type UserPath='/users';
键入PaymentPath='/payment';
类型CreatedAfter={
createdAfter:日期;
};
函数createRequest(
HttpVerb:string,
目标:用户路径
):(id:string,date:CreatedAfter)=>Promise;
函数createRequest(
HttpVerb:string,
目标:支付路径
//我只是猜测这里的返回类型
):(日期:CreatedAfter)=>承诺;
函数createRequest(HttpVerb:string,target:UserPath | PaymentPath):任意{
//您的函数实现不必是这样的,这就是这样
//这个例子完全有效
如果(目标=='/users'){
返回异步函数(日期){
返回{id:'1'};
};
}否则,如果(目标==='/付款'){
返回异步函数(id、日期){
返回[{id:'1'}];
};
}
}
//此签名与您的fetchUsers签名匹配
const fetchUsers=createRequest('GET','/users');
//此签名与您的付款签名匹配
const fetchPayment=createRequest('GET','/payment');

总之,这将允许
createRequest
函数根据传递的第二个参数返回具有正确签名的函数,按住ctrl+f键并搜索“重载”以了解有关重载的更多信息。

您可以这样继续:

// some interfaces you expect httpCall to return
interface User {
  name: string;
  age: number;
}
interface Payment {
  id: string;
}

// a mapping of request paths to the function signatures
// you expect to return from createRequest
interface Requests {
  "/users": (clause: { createdAfter: Date }) => Promise<Array<User>>;
  "/payments": (id: string, clause: { createdAfter: Date }) => Promise<Payment>;
}

// a dummy httpCall function
declare function httpCall<R>(path: string, method: string, payload: any): R;

// for now only GET is supported, and the path must be one of keyof Requests
function createRequest<P extends keyof Requests>(method: "GET", path: P) {
  return (function resourceApiCall(
    ...args: Parameters<Requests[P]> // Parameters<F> is the arg tuple of function type F
  ): ReturnType<Requests[P]> { // ReturnType<F> is the return type of function type F
    return httpCall<ReturnType<Requests[P]>>(path, method, args);
  } as any) as Requests[P]; // assertion to clean up createRequest signature
}

async function foo() {
  const fetchUsers = createRequest("GET", "/users");
  const users = await fetchUsers({ createdAfter: new Date() }); // User[]
  const fetchPayment = createRequest("GET", "/payments");
  const payment = await fetchPayment("id", { createdAfter: new Date() }); // Payment
}
然后你可以为你的
用户
制作一个模块:

请求/user.ts

export interface Requests extends Record<keyof Requests, (...args: any[]) => any> {
  // empty here, but merge into this 
}

// a dummy httpCall function
declare function httpCall<R>(path: string, method: string, payload: any): R;

// for now only GET is supported, and the path must be one of keyof Requests
export function createRequest<P extends keyof Requests>(method: "GET", path: P) {
  return (function resourceApiCall(
    ...args: Parameters<Requests[P]> // Parameters<F> is the arg tuple of function type F
  ): ReturnType<Requests[P]> {
    // ReturnType<F> is the return type of function type F
    return httpCall<ReturnType<Requests[P]>>(path, method, args);
  } as any) as Requests[P]; // assertion to clean up createRequest signature
}
export interface User {
  name: string;
  age: number;
}
declare module './requests' {
  interface Requests {
    "/users": (clause: { createdAfter: Date }) => Promise<Array<User>>;
  }
}
export interface Payment {
  id: string;
}
declare module './requests' {
  interface Requests {
    "/payments": (id: string, clause: { createdAfter: Date }) => Promise<Payment>;
  }
}
import { createRequest } from './Requests/requests';
import './Requests/user'; // maybe not necessary
import './Requests/payment'; // maybe not necessary

async function foo() {
  const fetchUsers = createRequest("GET", "/users");
  const users = await fetchUsers({ createdAfter: new Date() }); // User[]
  const fetchPayment = createRequest("GET", "/payments");
  const payment = await fetchPayment("id", { createdAfter: new Date() }); // Payment
}
等等。最后,用户可以通过导入
createRequest
user
payment
模块调用这些模块(如果其中包含代码,则需要在模块中运行):

测试.ts

export interface Requests extends Record<keyof Requests, (...args: any[]) => any> {
  // empty here, but merge into this 
}

// a dummy httpCall function
declare function httpCall<R>(path: string, method: string, payload: any): R;

// for now only GET is supported, and the path must be one of keyof Requests
export function createRequest<P extends keyof Requests>(method: "GET", path: P) {
  return (function resourceApiCall(
    ...args: Parameters<Requests[P]> // Parameters<F> is the arg tuple of function type F
  ): ReturnType<Requests[P]> {
    // ReturnType<F> is the return type of function type F
    return httpCall<ReturnType<Requests[P]>>(path, method, args);
  } as any) as Requests[P]; // assertion to clean up createRequest signature
}
export interface User {
  name: string;
  age: number;
}
declare module './requests' {
  interface Requests {
    "/users": (clause: { createdAfter: Date }) => Promise<Array<User>>;
  }
}
export interface Payment {
  id: string;
}
declare module './requests' {
  interface Requests {
    "/payments": (id: string, clause: { createdAfter: Date }) => Promise<Payment>;
  }
}
import { createRequest } from './Requests/requests';
import './Requests/user'; // maybe not necessary
import './Requests/payment'; // maybe not necessary

async function foo() {
  const fetchUsers = createRequest("GET", "/users");
  const users = await fetchUsers({ createdAfter: new Date() }); // User[]
  const fetchPayment = createRequest("GET", "/payments");
  const payment = await fetchPayment("id", { createdAfter: new Date() }); // Payment
}

好的,希望这能再次有所帮助。

这是一个巨大的帮助@jcalz!有没有一种方法可以让我在哪里内联声明预期的参数和返回类型,而不是使用
接口请求
?我不确定我是否理解。。。您可以改用类型别名(
type Requests={”/users):(子句:{createdAfter:Date}=>Promise;…
)但是你可能想给它起个名字,因为
请求
在签名和实现中多次出现,一遍又一遍地写那种类型可能会很烦人。如果我更了解你的用例,我可能会提出一个建议。你为什么不想要一个
请求
接口或类型呢?我会的因为我正在创建一个内部客户机sdk(http调用的抽象),所以我需要将各个类型定义放在同一个位置。我遵循大多数大型公司使用的模式,特别是stripe。在本例中,他们有一个
Charges
资源,可以简单地创建一个端点。我希望我的模式与此完全相同+内联预期的参数和端点的返回值。我的
createRequest
相当于St再次感谢您的帮助!因此,我建议保留
请求
界面,并使用它将端点添加到
请求
?让我看看是否可以重构上面的代码来演示…更新了我的答案,希望它能帮到更多。