【问题标题】:Function can return many types of responses. How do I declare the type of the response in the function that receives the response?函数可以返回多种类型的响应。如何在接收响应的函数中声明响应的类型?
【发布时间】:2019-06-23 19:51:35
【问题描述】:

有了这个类方法

async openProject(
    fileName: string, 
    force: boolean, 
    progress: boolean
) {
    const response = await this.request({
        type: 'request',
        cmd: 'open_project',
        payload: { value: fileName, force: force, progress: progress }
    });

    return {
        id: response['payload']['project_id']
    };
}

当我尝试访问响应时的有效负载属性时收到 TypeScript 警告:

[ts] 元素隐式具有“任何”类型,因为类型“{}”没有 索引签名。

我习惯于在函数调用中声明参数的类型。但是在这种情况下我应该如何声明响应的类型呢? this.requests 返回一个可以响应多种不同类型响应的 Promise。

我是否应该:

  • 为每种类型的请求创建响应类型并
  • this.request 返回所有响应类型的联合

编辑:

这是我在解决方案上的一次失败尝试

export type CreateProjectResponse = {
    payload: {
        project_id: string;
    }
}

export type GetDevicesResponse = {
    payload: {
        devices: [];
    }
}

export type GenericResponse = GetDevicesResponse | CreateProjectResponse;

request(body: any) {
    const transactionId = this.newTransactionId();

    const requestMessage = JSON.stringify({
        ...body,
        trans_id: transactionId
    });

    if (this.logger) this.logger(requestMessage);

    return new Promise<GenericResponse>((resolve, reject) => {
        const timeoutTimer = setTimeout(() => {
            const timeoutMessage = 'Request timed out';
            if (this.logger) this.logger(timeoutMessage);
            reject(Error(timeoutMessage));
        }, this.maxTime);

        this.requestCallbacks[transactionId] = {
            resolver: resolve,
            rejecter: reject,
            timeoutTimer: timeoutTimer
        };
        this.socket.send(requestMessage);
    });
}

async createProject() {
    const response = await this.request({
        type: 'request',
        cmd: 'create_project',
    }) ;

    return {
        id: response['payload']['project_id']
    };
};

async getDevices() {
    const response = await this.request({
        type: 'request',
        cmd: 'get_devices',
    });

    return response['payload']['devices'].filter((device: DeviceResponse) => {
        return device['type'] === 'device';
    }).map((device: DeviceResponse) => {
        return {
            id: device['device_id'],
            name: device['name']
        };
    });
};

【问题讨论】:

    标签: typescript


    【解决方案1】:

    这可以通过函数重载轻松完成...

    interface IRequest {
        type: 'request';
        cmd: 'create_project' | 'get_devices';
    }
    
    interface IResponse {
        id: string
    }
    
    interface CreateProjectRequest extends IRequest { cmd: 'create_project'; }
    interface GetDevicesRequest extends IRequest { cmd: 'get_devices'; }
    
    interface CreateProjectResponse extends IResponse { }
    interface GetDevicesResponce extends IResponse { name: string }
    
    function request(body: CreateProjectRequest) : CreateProjectResponse
    function request(body: GetDevicesRequest) : GetDevicesResponce
    function request<T extends IRequest>(body: T) : IResponse {
        return {} as IResponse;
    }
    
    var myCreateProjectResponse = request({ cmd: 'create_project', type: 'request' });
    var myGetDevicesResponse = request({ cmd: 'get_devices', type: 'request' });
    

    [DEMO]

    【讨论】:

    • 这太棒了
    【解决方案2】:

    您可以尝试将request 函数设为通用:

    function request<T = any>(): Promise<T> {
        //...
        return {} as Promise<T>;
    }
    
    interface MyData {
        prop: number;
    }
    
    async () => {
        const data = await request<MyData>();
        //data.prop - awailable in autocomplete
    }
    

    T = any 设置默认类型

    您可以在https://www.typescriptlang.org/docs/handbook/generics.html找到更多信息

    【讨论】:

      【解决方案3】:

      我认为正确的解决方案是对不同的可能答案类型使用用户定义的类型保护

      例子:

          interface Project: {
           project_id: number
          }
      
          function isProject(data: any): data is TypeA {
            return data.hasOwnProperty 
              && data.hasOwnProperty(project_id)
              && typeof data.project_id === "number"
          }
      
      

      这将允许 typescript compile 知道您收到的数据确实是 Project 类型。

          async openProject(
              fileName: string, 
              force: boolean, 
              progress: boolean
          ): number {
              const response = await this.request({
                  type: 'request',
                  cmd: 'open_project',
                  payload: { value: fileName, force: force, progress: progress }
              });
      
              const payload = response.payload
      
              if (isProject(payload)) {
                return payload.project_id
              }
          }
      

      您可以处理各种类型的结果,如果您未能成功针对已知类型验证有效负载,则可能应该抛出异常。

      有用的链接:

      https://basarat.gitbooks.io/typescript/docs/types/typeGuard.html

      https://github.com/epoberezkin/ajv

      实现类型保护的一个好方法是使用 json 模式库,如 ajv。

      https://spin.atomicobject.com/2018/03/26/typescript-data-validation/

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2020-04-06
        • 1970-01-01
        • 2020-04-25
        • 2020-05-13
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多