【问题标题】:Typescript return type depending on object parameter打字稿返回类型取决于对象参数
【发布时间】:2020-12-24 04:12:16
【问题描述】:

如何根据具有一个参数的函数正确推断返回类型,该参数是两种类型的并集?

我已经尝试了以下条件类型,但它不起作用(有关打字稿错误,请参阅内联注释):

TypeScript Playground

type Status = 'statusType'
type GoalActivity = 'goalActivityType'

type Argument = { type: 'status'; status: Status | null } | { type: 'goalActivity'; goalActivity: GoalActivity | null }

const handleReaction = (arg: Argument): Argument extends { type: "status" } ? Status : GoalActivity => {
    if (arg.type === 'status') {
        return 'statusType' // Type '"statusType"' is not assignable to type '"goalActivityType"'.
    } else {
        return 'goalActivityType'
    }
}

我还尝试了以下使用箭头函数的函数重载形式 (as described here),但这也会导致 TypeScript 错误并且还使用“any”,这会失去函数定义中的大部分打字优势:

TypeScript Playground

type Status = 'statusType'
type GoalActivity = 'goalActivityType'

type HandleReaction = {
    (arg: { type: 'status'; status: Status | null }): Status
    (arg: { type: 'goalActivity'; goalActivity: GoalActivity | null }): GoalActivity
}

const handleReaction: HandleReaction = (arg: any) => { // Type '"goalActivityType"' is not assignable to type '"statusType"'.
    if (arg.type === 'status') {
        return 'statusType'
    } else {
        return 'goalActivityType'
    }
}

这个问题和this one类似,不同的是函数参数是一个对象。

【问题讨论】:

    标签: typescript


    【解决方案1】:

    问题

    第一件事是你没有为你的参数使用泛型类型,这将导致typescript 永远不会根据你的输入推断出正确的类型(你可以想象泛型类型是参数,tsc 需要它来计算结果基于您的输入)。

    总之,

    const handleReaction = (arg: Argument): Argument extends { type: "status" } ? Status : GoalActivity => { // ... }

    将始终返回 Status | GoalActivity 作为返回类型。

    解决方案

    当然,您必须在这里使用泛型类型作为参数。我会用内联解释拆分你的代码:

    type Status = 'statusType'
    type GoalActivity = 'goalActivityType'
    
    type StatusObj = { type: 'status'; status: Status | null };
    type GoalActivityObj = { type: 'goalActivity'; goalActivity: GoalActivity | null }
    
    type Argument = StatusObj | GoalActivityObj;
    
    // Define returned type based on a input argument `T`
    type ReturnType<T> = T extends StatusObj ? Status : GoalActivity;
    
    // Generic type should be used here
    const handleReaction = <T extends Argument>(arg: T): ReturnType<T> => {
        if (arg.type === 'status') {
    
            // Q: Why do we have to cast here?
            // A: Any returned type can't assign to statement of `type ReturnType<T> ...`
            // but luckily `tsc` still allows us to cast back since they are all string literal
            return 'statusType' as ReturnType<T>;
    
        } else {
            return 'goalActivityType' as ReturnType<T>;
        }
    }
    

    【讨论】:

    • 啊,我想我明白了。太糟糕了,我们需要转换返回值,但我假设这是 TypeScript 的限制。
    猜你喜欢
    • 1970-01-01
    • 2019-09-01
    • 2020-04-23
    • 1970-01-01
    • 2021-07-18
    • 1970-01-01
    • 1970-01-01
    • 2022-01-12
    • 1970-01-01
    相关资源
    最近更新 更多