Typescript解决方法返回缩小的类型

时间:2018-01-10 01:06:28

标签: typescript

在typescipt中,我需要一种函数的方法,给定一个特定类型的参数,返回一个与参数类型相关的类型的对象。

请参阅下面的示例。我需要一种方法来制作const响应= ...'比它更狭窄的打字。

以下示例用于将特定类型的请求链接到仅与给定请求相关的响应。例如,给定查找用户信息的请求,我们希望获得包含其姓名和年龄的响应。但是当给出查找汽车信息的请求时,我们希望得到有关汽车的品牌和里程数的信息。我们只会想要使用'用户'对用户'的回复请求和类似的' car'。

class RequestBase {
}

class ResponseBase {
}

interface IFindUserReq {
    user_id :string
}
class FindUserRequest implements IFindUserReq {
    user_id :string
    constructor(user_id) {
        this.user_id = user_id
    }
}

interface IFindUserRes {
    name :string
    age  :number
}
class FindUserResponse implements IFindUserRes {
    name :string
    age  :number
    constructor(name, age) {
        this.name = name;
        this.age = age;
    }
}

interface IFindCarReq {
    car_id :number
}
class FindCarRequest implements IFindCarReq {
    car_id :number 
    constructor(car_id) {
        this.car_id = car_id
    }
}

interface IFindCarRes {
    make :string
    miles :number
}
class FindCarResponse implements IFindCarRes {
    make :string
    miles  :number
    constructor(make, miles) {
        this.make = make;
        this.miles = miles;
    }
}

const request = new FindUserRequest("foo")
const response = performRequest(request) // the type here is 'RequestBase | undefined'. Is there any way to automatically narrow it to be FindCarResponse?

function performRequest(req :RequestBase) : RequestBase | undefined {
    if (req instanceof FindUserRequest) {
        return new FindUserResponse("foo", 23) // hard coded example for convenience
    } else if (req instanceof FindCarRequest) {
        return new FindCarResponse("toyota", 10000)
    }
}

更新:解决方案1 ​​ 灵感来自Variable return types based on string literal type argument

一种方法是重载'performRequest'签名如此:

function performRequest(req :FindCarRequest) : FindCarResponse 
function performRequest(req :FindUserRequest) : FindUserResponse
function performRequest(req :RequestBase) : ResponseBase | undefined   {
    if (req instanceof FindUserRequest) {
        return new FindUserResponse("foo", 23) // hard coded example for convenience
    } else if (req instanceof FindCarRequest) {
        return new FindCarResponse("toyota", 10000)
    }
}

但是,我真的希望维护请求和响应类型的库不必更改使用请求和响应类型(performRequest)的应用程序中的函数签名。所以我仍然希望听到其他解决方案。

更新解决方案2 感谢来自TS Gitter频道的Gerrit Birkeland:

class RequestBase {
    _responseType : ResponseBase
}

class ResponseBase {
}

interface IFindUserReq {
    user_id :string
}
class FindUserRequest extends RequestBase implements IFindUserReq {
    _responseType :FindUserResponse
    user_id :string
    constructor(user_id) {
        super()
        this.user_id = user_id
    }
}

interface IFindUserRes {
    name :string
    age  :number
}
class FindUserResponse extends ResponseBase implements IFindUserRes {
    name :string
    age  :number
    constructor(name, age) {
        super()
        this.name = name;
        this.age = age;
    }
}

interface IFindCarReq {
    car_id :number
}
class FindCarRequest extends RequestBase implements IFindCarReq {
    _responseType :FindCarResponse
    car_id :number 
    constructor(car_id) {
        super()
        this.car_id = car_id
    }
}

interface IFindCarRes {
    make :string
    miles :number
}
class FindCarResponse extends ResponseBase implements IFindCarRes {
    make :string
    miles  :number
    constructor(make, miles) {
        super()
        this.make = make;
        this.miles = miles;
    }
}

const request = new FindUserRequest("foo")
const response = performRequest<FindUserRequest>(request) // the type of response here is ResponseBase, not sure why it's not narrowed 

function performRequest< T extends RequestBase>(req :T) :T["_responseType"]    {

    if (req instanceof FindUserRequest) {
        return new FindUserResponse("foo", 23) // hard coded example for convenience
    } else if (req instanceof FindCarRequest) {
        return new FindCarResponse("toyota", 10000)
    } else {
        return new ResponseBase()
    }
}

2 个答案:

答案 0 :(得分:3)

您可以(通常)通过向Option Explicit Sub test() With thisworkbook ' The first cell to stack downward from (will include the cell you specify), plus sheet name.' Dim OutputSheetRange as range Set OutputSheetRange = .worksheets("Sheet2").range("A1") ' Change this to the worksheet containing the cells that need to be stacked.' With .Worksheets("Sheet1") Dim FormulaPrefix as string FormulaPrefix = "='" & .name & "'!" ' Code assumes range below, but change to whatever you need.' With .range("A1:D4") Dim RowCount as long RowCount = .rows.count Dim ColumnCount as long ColumnCount = .columns.count Dim ColumnIndex as long Dim RowIndex as long Dim OutputIndex as long OutputIndex = 0 For ColumnIndex = 1 to ColumnCount For RowIndex = 1 to RowCount OutputSheetRange.offset(OutputIndex,0).formula = FormulaPrefix & .cells(rowindex,columnindex).address OutputIndex = OutputIndex + 1 Next RowIndex Next Columnindex End With End with End Sub 类添加属性来实现所需的效果。此属性不需要用于任何内容,但它确实需要存在。

(从我的gitter消息中复制)

textBox1.Text = Properties.Settings.Default.TextBoxDefaultValue;

答案 1 :(得分:0)

如果您可以将RequestBase更改为通用,则可以添加表示响应类型的泛型类型参数。然后,您可以使用它来返回响应类型。

class RequestBase<TResponse> {
    performRequest() : TResponse {
        return null; // Dummy, could invoke the actual performRequest
    }
}
class FindCarRequest extends RequestBase<FindCarResponse> {
    constructor(public car_id: number) {
        super();
    }
}
class FindCarResponse {
    constructor(public make: string, public miles: number) {
    }
}

const request = new FindCarRequest(100)
const response = request.performRequest() // Will be typed as FindCarResponse

注意不幸的是,将performRequest函数保留为外部函数不是一个选项,因为类型推断不够智能,无法从基类型中获取泛型类型参数:

function performRequest<T>(req: RequestBase<T>):T {
    return null; // Dummy
}

const request = new FindCarRequest(100)
const response = performRequest(request) // Will be typed as {} although I would have expected this to be FindCarResponse

const request2 = new RequestBase<FindCarResponse>()
const response2 = performRequest(request) // Will be typed as FindCarResponse