typescript中的可调用类型关系出现问题



TypeScript中可调用类型之间的关系存在以下问题:

type CheckExtends<T, U> = T extends U ? T : never
type NumberFunc = (op: number) => number
type AnyFunc = (op: any) => any;
type UnknownFunc = (op: unknown) => any;
type NumberAny = CheckExtends<NumberFunc, AnyFunc> // NumberFunc
type NumberUnknown = CheckExtends<NumberFunc, UnknownFunc> // never

(在操场上(

通过注释表明,NumberFunc类型是对AnyFunc类型的扩展,而不是对UnknownFunc类型的扩展。

我不明白,因为NumberFunc类型(number(的参数类型可分配给anyunknown类型

一旦引入any,就会失去TypeScript中的许多类型安全保证。any类型故意不健全,允许您执行编译器认为不安全的操作。所有内容都可分配给,并且可从any分配;这是打字系统的逃生通道。因此,CCD_ 11被视为可分配给CCD_ 12并不奇怪。您还可以看到AnyFunc可分配给NumberFunc:

type AnyNumber = CheckExtends<AnyFunc, NumberFunc> // AnyFunc

那么,最好忽略any,然后使用类型安全的unknown继续问题的另一部分:为什么NumberFunc不能分配给UnknownFunc


为了保持类型安全,函数的参数类型应该是相反的。这意味着函数类型之间的子类型/超类型关系与其参数的关系相反。因此,例如,当numberunknown子型时,类型(x: number) => void的函数是(x: unknown) => void超类型。因此,可分配关系发生了逆转。一开始这可能令人困惑,但实际上这是从类型安全角度来看唯一有意义的事情。这是利斯科夫替代原则的结果;如果A可分配给B,则意味着您可以A类型的值替换任何类型为B的值,并且不会导致任何错误或问题。

假设我有一个类型为number的值n。如果你要求unknown类型的值,如果我给你n,你会很高兴的。如果我递给你"hello"false或任何值,你会很高兴的。因为numberunknown的一个子类型,所以您可以在任何需要unknown的地方使用number

但是现在假设我有一个(x: number) => void类型的值f。如果你要求一个类型为(x: unknown) => void的值,而我递给你f,当你试图使用它时,你可能会非常不高兴。你认为你有一个函数可以接受任何可能的参数,所以你希望能够像f("hello")f(false)f(15)那样调用它。但是f()只接受number参数,所以f("hello")f(false)在运行时很可能会爆炸。您所要求的函数是非常特定的。因为在某些地方不能使用(x: number) => void,所以可以使用(x: unknown) => void,因此前者是,而不是后者的子类型。

事实上恰恰相反:如果我有一个类型为(x: unknown) => void的值g,而你要求一个类型(x: number) => void的值,我可以递给你g,你会很高兴的。您将只使用数字参数(如g(1)g(2)g(15)(调用g(),因此在运行时永远不会出现问题。这意味着UnknownFuncNumberFunc:的一个亚型

type UnknownNumber = CheckExtends<UnknownFunc, NumberFunc> // UnknownFunc

好的,希望能有所帮助;祝你好运

游乐场链接到代码

正如您在本文中看到的,只有当启用strictFunctionChecks时,目标函数的参数可分配给源函数的参数时,typescript函数才可分配。例如,如果您有以下代码:

interface A {
a: number;
}
interface B extends A {
b: number;
}
type AFunc = (op: A) => any;
type BFunc = (op: B) => any;
let bf: BFunc = x => x;
let af: AFunc = bf;

由于A(目标参数类型(不可分配给B(源参数类型(,因此无法将BFunc分配给Afunc,因此会出现此错误。注意,检查AFunc extends BFunc是否等同于检查AFunc是否可分配给BFunc。不能将NumberFunc分配给UnknownFunc,因为目标参数类型unknown不能分配给源参数类型number。这意味着NumberFunc不扩展UnknownFunc。如果在操场上禁用strictFunctionChecks,则可以看到NumberUnknown不再是never,而是NumberFunc

最新更新