You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

TypeScript中如何在子类推断已解析泛型Result的具体类型

实现方案

这个需求完全可以实现,你之前的代码存在几个核心问题导致类型推断失效,逐一修正即可。

原有代码问题梳理

  • Success类的isError类型守卫逻辑写反了,错误返回true会导致类型收窄完全失效
  • 抽象工厂的泛型没有加约束,TS无法识别传入参数是Result类型,自然读不到isError()、value()方法
  • 之前写的typeof Failure/typeof Success是获取类的构造函数类型,不是类实例类型,和实际传入的参数类型不匹配
  • 子类中写两个同名parseSuccess方法不符合TS语法规则,类不支持同名方法的多实现,错误、成功分支需要拆分为两个独立方法处理

修正后的完整实现

1. 先修复基础Result类的类型守卫bug

export type Result<E, S> = Failure<E, S> | Success<E, S>;

export class Failure<E, S> {
    constructor(private _value: E) {}

    isError(): this is Failure<E, S> {
        return true;
    }

    value(): E {
        return this._value
    }

    isSuccess(): this is Success<E, S> {
        return false;
    }
}

export class Success<E, S> {
    constructor(private _value: S) {}

    value(): S {
        return this._value;
    }

    // 修复:错误场景才返回true,成功场景isError必须返回false
    isError(): this is Failure<E, S> {
        return false;
    }

    isSuccess(): this is Success<E, S> {
        return true;
    }
}

2. 改造通用HTTP响应抽象工厂

通过infer条件类型从传入的Result泛型中自动提取错误值、成功值类型,同时给泛型加约束确保类型合法:

// 工具类型:从Result中提取错误值类型
type ExtractErrorType<T> = T extends Result<infer E, any> ? E : never;
// 工具类型:从Result中提取成功值类型
type ExtractSuccessType<T> = T extends Result<any, infer S> ? S : never;

abstract class HttpResponseFactory<T extends Result<any, any>> {
    public make(result: T): HttpResponse {
        if (result.isError()) {
            // 类型自动收窄为错误分支,value()返回类型自动推断为对应错误类型
            return this.parseError(result.value());
        }
        // 类型自动收窄为成功分支,value()返回类型自动推断为对应成功类型
        return this.parseSuccess(result.value());
    }

    // 默认错误处理返回500,子类可重写自定义不同错误的状态码、返回结构
    protected parseError(error: ExtractErrorType<T>): HttpResponse {
        return response.status(HttpCode.InternalServerError).json({})
    }

    // 要求子类必须实现成功场景的响应逻辑
    protected abstract parseSuccess(result: ExtractSuccessType<T>): HttpResponse;
}

3. 子类实现示例

子类只需要传入对应的Result类型作为泛型参数,TS会自动推断parseError、parseSuccess的参数类型,不需要手动声明:

// 原有业务类型定义
class UserAlreadyExistsError extends Error {}
class UnexpectedError extends Error {}
class User {
   constructor(private name: string) {}
}
type CreateUserResult = Result<UserAlreadyExistsError | UnexpectedError, User>;

class CreateUserHttpResponseFactory extends HttpResponseFactory<CreateUserResult> {
    // 参数自动推断为User类型
    protected parseSuccess(user: User): HttpResponse {
        return response.status(HttpCode.Created).json({
            code: HttpCode.Created,
            data: user,
            message: "用户创建成功"
        })
    }

    // 参数自动推断为UserAlreadyExistsError | UnexpectedError类型
    protected override parseError(error: UserAlreadyExistsError | UnexpectedError): HttpResponse {
        if (error instanceof UserAlreadyExistsError) {
            return response.status(HttpCode.Conflict).json({
                code: HttpCode.Conflict,
                message: "用户已存在"
            })
        }
        // 其余错误走默认500逻辑
        return super.parseError(error);
    }
}

使用效果

控制器层注入工厂后调用完全符合类型校验,不需要额外的类型断言:

class CreateUserController {
    constructor(private createUserHttpResponseFactory: CreateUserHttpResponseFactory) {}

    create(request: HttpRequest): HttpResponse {
        const createUserResult = new CreateUserUseCase().execute({});
        // 入参自动校验为CreateUserResult类型,返回值自动识别为HttpResponse
        return this.createUserHttpResponseFactory.make(createUserResult);
    }
}

内容的提问来源于stack exchange,提问作者Thiago Dias

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.26 14:21:27