Typescript 功能“;缺少返回语句”;但是所有的路径都有打字机

Typescript 功能“;缺少返回语句”;但是所有的路径都有打字机,typescript,Typescript,我有以下接口和类型(所有接口和类型都可以更改) 接口基础{ 类型:字符串; } 接口A扩展了基础{ 类型:“A”; 字段:字符串; } 接口B扩展了基础{ 类型:“B”; 查询:字符串; } 接口C扩展了基底{ 类型:“C”; 相等:数字; } 类型BaseExtensions=A | B | C; 接口基本包装器{ idType:字符串; 碱基:R; } 接口和{ 类型:“和”; 规则:数组; } 接口或{ 类型:“或”; 规则:数组; } 类型规则=BaseWrapper |和|或 我想做

我有以下接口和类型(所有接口和类型都可以更改)

接口基础{
类型:字符串;
}
接口A扩展了基础{
类型:“A”;
字段:字符串;
}
接口B扩展了基础{
类型:“B”;
查询:字符串;
}
接口C扩展了基底{
类型:“C”;
相等:数字;
}
类型BaseExtensions=A | B | C;
接口基本包装器{
idType:字符串;
碱基:R;
}
接口和{
类型:“和”;
规则:数组;
}
接口或{
类型:“或”;
规则:数组;
}
类型规则=BaseWrapper |和|或
我想做的是编写以下函数:

function doAThingBasedOnTheRuleType(rule: Rule<any>): Thing {
    if (isAnd(rule)) {
        return DoAndThing(rule);
    }
    if (isOr(rule)) {
        return DoOrThing(rule);
    }
    if (isA(rule.base)) {
        return DoAThing(rule);
    }
    if (isB(rule.base)) {
        return DoBThing(rule);
    }
    if (isC(rule.Base)) {
        return DoCThing(rule);
    }
    // error, [ts] Function lacks ending return statement and return type does not include 'undefined'.

}
函数dothingbasedontheruletype(规则:rule):Thing{
if(isAnd(规则)){
返回DoAndThing(规则);
}
if(isOr(规则)){
返回门(规则);
}
if(isA(规则基础)){
退货(规则);
}
if(isB(规则基础)){
退货(规则);
}
if(isC(规则基础)){
返回文档(规则);
}
//错误,[ts]函数缺少结束返回语句,返回类型不包括“undefined”。
}
我希望这个规则从
和|或| BaseWrapper | BaseWrapper | BaseWrapper
开始,应该一个接一个地缩小范围。但是,我得到了错误
//error,[ts]函数缺少结束返回语句,返回类型不包括“undefined”。

1-为什么TS无法推断类型? 2-如何修复它


我使用的是TS 2.5.2

TypeScript关于隐式返回的规则是在语法上强制执行的——在不知道涉及的类型的情况下,函数的所有可到达的出口点都需要一个return语句

要确定使用类型系统无法访问给定函数的隐式返回,需要多次“传递”,而类型系统目前没有这样做(出于性能/复杂性原因)

例如,考虑这个代码:

function fn1() {
    const f = fn2();
    if (f === "alpha") {
        return "A";
    } else if (f === "beta") {
        return "B";
    }
}

function fn2() {
    const f = fn1();
    if (f === "A") {
        return "alpha";
    } else if (f === "B") {
        return "beta";
    }
    return "gamma";
}
什么类型的
fn1()
?如果
fn2()
返回的值不是
“alpha”
“beta”
,则它可能是
“A”|“B”|未定义的
,或者如果这些是唯一的返回值,则它可能是
“A”|“B”
。那么,让我们检查一下
fn2()
——它的返回类型是什么?这取决于
fn1()的类型--
fn2
如果
fn1
仅返回
“A”|“B”
,则返回
,如果
未定义的
是可能的返回值,则返回
“alpha”|“beta”|“gamma”

因此,为了弄清楚
fn1
隐式返回的可达性,您必须进行多次“循环”推理,根据另一个函数的类型细化一个函数的类型,然后重复,希望您达到一个固定点,而不是无限递归。这比使用隐式返回的语法强制执行进行单次传递要昂贵得多

最简单的修复方法是简单地添加一个
抛出

    } else if (...) {
       return ...;
    }
    throw new Error("Shouldn't be reachable");
}
或者,如果您确实对代码覆盖率感兴趣,请将
if
条件重写为最后一个块中的断言:

   } else {
       Debug.assert(x.kind === "B");
       return "C";
   }

如果您不打算抛出任何错误,并且
isC
是您的最后一个规则检查点,您能否删除该条件(因此总是在最后返回CThing)

函数dothingbasedontheruletype(规则:rule):Thing{
if(isAnd(规则)){
返回DoAndThing(规则);
}
if(isOr(规则)){
返回门(规则);
}
if(isA(规则基础)){
退货(规则);
}
if(isB(规则基础)){
退货(规则);
}
返回文档(规则);
}

简短回答:好的,这是我的直觉,但我想知道为什么。您的示例版本与我的版本不同。在我的版本中,我们提前知道输入是A | B |类型的有限集合。。。(为了简单起见),每个typeguard都应该过滤掉它。一旦我完成了,如果(isA(rule)){…return}TS知道规则不再是类型A。类似地,在类型B、C等中。一旦没有剩下的类型,它就不能再被访问。我想我遗漏了一些复杂的情况,也许它不像你提到的那样是完全可概括的。在编程语言中,你很少会发现只有简单的情况起作用的行为。如果编译器不能使某些行为在所有(或几乎所有)情况下都能工作,那么它根本就不能工作。语言希望非常可预测,而特殊的大小写简单场景会很快降低可预测性。这会使许多代码变得更糟,强制使用默认情况。如果我以后向联合添加一个新类型,我希望编译器给我所有需要添加代码以处理新类型的位置。有时,不可能提供合理的违约。很少可能提供适用于所有新类型的默认值。控制流分析应该能够处理这一问题,并指出如果添加了
else
,它将永远不会实际运行,因为没有
else
案例。获得这一点很好!在某些情况下,用眼睛很容易看到,也许这些情况更容易被检测到,否则在更复杂的情况下,会返回报告错误?
   } else {
       Debug.assert(x.kind === "B");
       return "C";
   }
function doAThingBasedOnTheRuleType(rule: Rule<any>): Thing {
    if (isAnd(rule)) {
        return DoAndThing(rule);
    }
    if (isOr(rule)) {
        return DoOrThing(rule);
    }
    if (isA(rule.base)) {
        return DoAThing(rule);
    }
    if (isB(rule.base)) {
        return DoBThing(rule);
    }

    return DoCThing(rule);
}