TypeScript 从不进行类型推断
TypeScript never type inference
谁能解释一下为什么给出以下代码:
let f = () => {
throw new Error("Should never get here");
}
let g = function() {
throw new Error("Should never get here");
}
function h() {
throw new Error("Should never get here");
}
推断出以下类型:
f
是 () => never
g
是 () => never
h
是 () => void
我希望 h
的类型也是 () => never
。
谢谢!
好问题。区别在于f
和g
是函数表达式,其中h
是函数声明。当一个函数是 throw
-only 时,如果它是一个表达式,它得到类型 never
,如果它是一个声明,它得到类型 void
。
上面的段落当然没有实际帮助。为什么函数表达式和函数声明之间的行为存在差异?让我们看看每种情况下的一些反例。
坏主意 #1:制作抛出函数表达式 return void
考虑一些代码:
function iif(value: boolean, whenTrue: () => number, whenFalse: () => number): number {
return value ? whenTrue() : whenFalse();
}
let x = iif(2 > 3,
() => { throw new Error("haven't implemented backwards-day logic yet"); },
() => 14);
这个代码可以吗?它应该是!当我们认为不应调用该函数或仅应在错误情况下调用该函数时,通常会编写 throw
ing 函数。但是,如果函数表达式的类型是 void
,则对 iif
的调用将被拒绝。
所以从这个例子中可以清楚地看出,只有 throw
的函数表达式应该 return never
,而不是 void
。实际上这应该是我们的默认假设,因为这些函数符合 never
的定义( 在正确类型的程序中,无法观察到类型 never
的值).
坏主意 #2:进行抛出函数声明 return never
读完前面的部分后,您应该会说 "Great, why don't all throwing functions return never
, then?"
简短的回答是,这样做是一个重大的突破性变化。那里有很多代码(尤其是 abstract
关键字之前的代码)看起来像这样
class Base {
overrideMe() {
throw new Error("You forgot to override me!");
}
}
class Derived extends Base {
overrideMe() {
// Code that actually returns here
}
}
但是 returning void
函数不能替代 returning never
函数(记住,在正确键入的程序中,never
值无法观察到),因此使 Base#overrideMe
return never
阻止 Derived
提供该方法的任何非 never
实现。
通常,虽然总是抛出异常的函数 表达式 通常作为 Debug.fail
的占位符存在,但函数 声明 总是扔的非常罕见。表达式经常被别名或忽略,而声明是静态的。今天 throw
的函数 声明 实际上明天可能会做一些有用的事情;在没有 return 类型注释的情况下,提供的更安全的东西是 void
(即不要看这个 return 类型)而不是 never
(即这个函数是一个黑洞,会吃掉当前的执行堆栈。
谁能解释一下为什么给出以下代码:
let f = () => {
throw new Error("Should never get here");
}
let g = function() {
throw new Error("Should never get here");
}
function h() {
throw new Error("Should never get here");
}
推断出以下类型:
f
是() => never
g
是() => never
h
是() => void
我希望 h
的类型也是 () => never
。
谢谢!
好问题。区别在于f
和g
是函数表达式,其中h
是函数声明。当一个函数是 throw
-only 时,如果它是一个表达式,它得到类型 never
,如果它是一个声明,它得到类型 void
。
上面的段落当然没有实际帮助。为什么函数表达式和函数声明之间的行为存在差异?让我们看看每种情况下的一些反例。
坏主意 #1:制作抛出函数表达式 return void
考虑一些代码:
function iif(value: boolean, whenTrue: () => number, whenFalse: () => number): number {
return value ? whenTrue() : whenFalse();
}
let x = iif(2 > 3,
() => { throw new Error("haven't implemented backwards-day logic yet"); },
() => 14);
这个代码可以吗?它应该是!当我们认为不应调用该函数或仅应在错误情况下调用该函数时,通常会编写 throw
ing 函数。但是,如果函数表达式的类型是 void
,则对 iif
的调用将被拒绝。
所以从这个例子中可以清楚地看出,只有 throw
的函数表达式应该 return never
,而不是 void
。实际上这应该是我们的默认假设,因为这些函数符合 never
的定义( 在正确类型的程序中,无法观察到类型 never
的值).
坏主意 #2:进行抛出函数声明 return never
读完前面的部分后,您应该会说 "Great, why don't all throwing functions return never
, then?"
简短的回答是,这样做是一个重大的突破性变化。那里有很多代码(尤其是 abstract
关键字之前的代码)看起来像这样
class Base {
overrideMe() {
throw new Error("You forgot to override me!");
}
}
class Derived extends Base {
overrideMe() {
// Code that actually returns here
}
}
但是 returning void
函数不能替代 returning never
函数(记住,在正确键入的程序中,never
值无法观察到),因此使 Base#overrideMe
return never
阻止 Derived
提供该方法的任何非 never
实现。
通常,虽然总是抛出异常的函数 表达式 通常作为 Debug.fail
的占位符存在,但函数 声明 总是扔的非常罕见。表达式经常被别名或忽略,而声明是静态的。今天 throw
的函数 声明 实际上明天可能会做一些有用的事情;在没有 return 类型注释的情况下,提供的更安全的东西是 void
(即不要看这个 return 类型)而不是 never
(即这个函数是一个黑洞,会吃掉当前的执行堆栈。