代码之家  ›  专栏  ›  技术社区  ›  user1283776

函数可以返回多种类型的响应。如何在接收响应的函数中声明响应的类型?

  •  0
  • user1283776  · 技术社区  · 6 年前

    用这个类方法

    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']
        };
    }
    

    当我尝试访问响应时的Payload属性时,会收到typescript警告:

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

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

    我应该:

    • 为每种类型的请求创建响应类型,并
    • 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']
            };
        });
    };
    
    3 回复  |  直到 6 年前
        1
  •  2
  •   Matěj Pokorný    6 年前

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

    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
  •  0
  •   Oleksandr    6 年前

    你可以试着做 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
  •  0
  •   Antoine Drouhin    6 年前

    我认为适当的解决办法是 使用用户定义的类型保护 对于不同的可能答案类型。

    例子:

        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编译知道您收到的数据确实是项目类型。

        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

    实现类型保护的一个好方法是使用类似AJV的JSON模式库。

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