为什么TypeScript显示不可执行路径的错误?
我有以下示例代码:为什么TypeScript显示不可执行路径的错误?,typescript,Typescript,我有以下示例代码: function func(a?: number, b?: number, c?: number) { let allGood: boolean = true; if(!a) { console.log('a is unacceptable, so do something with a') allGood = false; } if(!b) { console.log('b is unacceptable, so do som
function func(a?: number, b?: number, c?: number) {
let allGood: boolean = true;
if(!a) {
console.log('a is unacceptable, so do something with a')
allGood = false;
}
if(!b) {
console.log('b is unacceptable, so do something with b')
allGood = false;
}
if(!c) {
console.log('a is unacceptable, so do something with c')
allGood = false;
}
if(allGood) {
console.log(a + b + c) // Error: "Object is possibly undefined" showing for a, b and c
} else {
console.log('oh no!')
}
}
您可以看到,只有当所有
a
、b
和c
都不正确(包括未定义的)时,最后的allGood
才是正确的。但在最后一个条件中,TypeScript抱怨a
、b
和c
中的每一个都可能未定义。在不重新检查a
、b
和c
的真实性的情况下,解决这个问题的最佳方法是什么?因为它们在这种情况之前已经被检查过了。简单回答:控制流缩小本质上是启发式的,并且不考虑变量之间的这种相关性。在请求支持所谓的“分支标志”(如allGood
)时存在一个公开问题。但这是为了实施
长版本:
TypeScript用于缩小代码块中变量和属性的明显类型,在代码块中,它可以遵循已发生的类型保护或赋值。因此,例如,在下面的示例代码中,编译器可以看到对a
的真实性检查将把它从number | undefined
缩小到number
:
if (!a) { } else {
a + 1; // okay
}
但是在if
语句的true和false分支完成后,如果来自多个路径的控制流再次合并,编译器将把缩小的类型重新合并到来自每个分支的任何缩小类型中:
let x: number | string | undefined = Math.random() < 0.5 ? 123 : undefined;
// x is number | undefined
if (typeof x === "undefined") {
// x is undefined
x = "hello"; // x is string
} else {
x += 789; // x is number
}
// x is string | number
x.valueOf(); // okay
(在这里,我使用了更简洁的(a作为数字)+(b作为数字)+(c作为数字)
)
或者通过重构到一个不依赖于这些相关变量的版本,例如(无可否认是奇怪的):
显然,在本例中,断言不那么突出,因此这将是我的建议
对于类型系统来说,这是一个相对复杂的问题。虽然简单的推理“显而易见”,但它涉及到观察对单独变量的跟踪变化(类似于执行程序…)。如果(!a&&!b&&!c)
,询问和回答会容易得多。我可能会重写此代码以消除allGood
变量,并将失败的检查和特定处理移到else分支;或者对无效案例使用范围中断构造(return
),这将导致预期的TS行为。什么版本的Typescript?我正在复制,没有错误。
let someBoolean: boolean;
let x: number | string | undefined = Math.random() < 0.5 ? 123 : undefined;
// x is number | undefined
if (typeof x === "undefined") {
// x is undefined
x = "hello"; // x is string
someBoolean = true;
} else {
x += 789; // x is number
someBoolean = false;
}
// x is string | number
x.valueOf(); // okay
someBoolean; // boolean
if (allGood) {
console.log(a! + b! + c!) // assertions
} else {
console.log('oh no!')
}
function func(a?: number, b?: number, c?: number) {
let good = (a: number) => (b: number) => (c: number) => () => console.log(a + b + c);
let aGood;
if (!a) {
console.log('a is unacceptable, so do something with a')
} else {
aGood = good?.(a);
}
let abGood;
if (!b) {
console.log('b is unacceptable, so do something with b')
} else {
abGood = aGood?.(b)
}
let abcGood;
if (!c) {
console.log('c is unacceptable, so do something with c')
} else {
abcGood = abGood?.(c)
}
if (abcGood) {
abcGood();
} else {
console.log('oh no!')
}
}
func(1, 2, 3) // 6;
func(1) // b unacceptable, c unacceptable, oh no!