Typescript 如何理解any、unknown、{}类型之间以及它们与其他类型之间的关系?
试图理解类型之间的关系我有这个代码Typescript 如何理解any、unknown、{}类型之间以及它们与其他类型之间的关系?,typescript,types,Typescript,Types,试图理解类型之间的关系我有这个代码 type CheckIfExtends<A, B> = A extends B ? true : false; type T1 = CheckIfExtends<number, unknown>; //true type T2 = CheckIfExtends<number, {}>; //true type T3 = CheckIfExtends<number, any>; //true type T4 =
type CheckIfExtends<A, B> = A extends B ? true : false;
type T1 = CheckIfExtends<number, unknown>; //true
type T2 = CheckIfExtends<number, {}>; //true
type T3 = CheckIfExtends<number, any>; //true
type T4 = CheckIfExtends<() => void, unknown>; //true
type T5 = CheckIfExtends<() => void, {}>; //true
type T6 = CheckIfExtends<() => void, any>; //true
type T7 = CheckIfExtends<unknown, any>; //true
type T8 = CheckIfExtends<any, unknown>; //true
type T9 = CheckIfExtends<{}, unknown>; //true
type T10 = CheckIfExtends<{}, any>; //true
type T11 = CheckIfExtends<any, {}>; //boolean
type T12 = CheckIfExtends<unknown, {}>; //false
我将从TypeScript中的
扩展
的含义开始。乍一看,它的行为很奇怪,对于产品类型(如对象)来说,它的行为类似于“is超集”,对于unions
as“is子集”,它的工作方式也完全不同于函数类型。一开始它可能看起来很奇怪,但它是逻辑行为,换句话说,大多数类型都有
我理解这个概念的经验法则是将扩展为可分配给。然后,如果x
扩展y
,则意味着只要需要y
,就可以使用x
让我们考虑三种不同的代数数据类型,如果它们是正确的。< /P>
用于产品类型
type A = {a: string}
type B = {a: string; b: number}
type BextendsA = B extends A ? true : false // evaluates to true
type A = number | string
type B = number
type BextendsA = B extends A ? true : false // evaluates to true
以上是正确的,因为B可以在需要A的每个地方使用,因为B覆盖了A的整个结构。B是a的超集,但这里所包含的是B可以赋值给a
用于活接头类型
type A = {a: string}
type B = {a: string; b: number}
type BextendsA = B extends A ? true : false // evaluates to true
type A = number | string
type B = number
type BextendsA = B extends A ? true : false // evaluates to true
完全不同的是,它寻求联盟。对于产品B是一个超集,对于联合体B是一个子集!是的,不同的逻辑,B并不代表A中所有可能的值,但只要需要A,B就可以使用。所以B可以分配给A
对于功能类型
type A = (a: number) => void
type B = () => void
type BextendsA = B extends A ? true : false // evaluates to true
对于函数类型,它看起来更奇怪,因为A看起来比B更像指定的函数,那么B如何扩展A呢?这又是从可分配性开始的,因为无论什么时候需要A,我们都可以分配B。这在像Array.map
这样的示例中非常明显。考虑:
[1,2].map(x => x + 1)
Array.map需要有三个参数的函数-(el,index,arr)=>any
,但可以使用只有一个参数的函数el=>any
。它同样具有可转让性,B可转让给A
像any
、unknown
、{}
这样的类型是不可靠的,这意味着它们的行为无法在逻辑上得到证明。理解他们在TS中的行为更像是理解这些决策的规范和原因。但它不能从逻辑上解释,因为不健全的类型违背逻辑
我们仔细考虑了TypeScript允许不健全行为的地方,在本文档中,我们将解释这些情况发生的地方以及背后的激励场景
区别主要在于:
- 是故意不合理的,因为它既可分配给任何其他类型,也可从中分配给任何其他类型(可能的例外是
从不
,这取决于您使用它的位置)。不健全意味着类型的一些基本规则被破坏,例如of。通常,如果A
可分配给B
,而B
可分配给C
,则A
可分配给C
。但是any
打破了这一点。例如:字符串
可分配给任何
,而任何
可分配给数字
。。。但是字符串
不能分配给数字
。这种特殊的不健全是非常有用的,因为它允许我们从本质上“关闭”代码中很难或不可能正确键入的部分的类型检查。但是你需要非常小心地把任何看作一种类型;这更像是一种“联合国类型”
- 是一种可以在运行时像对象一样处理的类型(也就是说,您可以从中读取属性或方法,而不会出现运行时错误),但在编译时它没有已知的属性。这并不意味着它没有属性;这只是意味着编译器不知道它们中的任何一个。这意味着只有
null
和undefined
不能分配给{}
(null.foo
或undefined.foo
是运行时错误)。甚至像string
这样的基本类型在运行时也可以被视为具有属性和方法(.length
和.toUpperCase()
有效,甚至.foo
只返回未定义的)。当然,任何实际的对象类型也将被分配给{}
另一方面,{}
类型不能分配给很多类型。如果我将类型为{}
的值分配给类型为{foo:string}
的变量,则会出现编译器错误,因为{}
不包含foo
属性。您可以将{}
分配给自身,或分配给更广泛的类型,如未知
,或分配给“untype”任意
这使得{}
非常接近,这是一种所有其他类型都可分配的类型。它本质上是一个top类型,从中删除了null
和undefined
- 是在TypeScript 3.0中引入的,是真正的顶级类型;TypeScript中的每种类型都可分配给
未知
。甚至null
和undefined
也可分配给unknown
另一方面,unknown
仅可分配给自身和“untype”any
。即使是{}
类型也不够宽,您无法将unknown
分配给它。从概念上讲,您应该能够将unknown
分配给联合类型{}null | undefined
,但这是为了将unknown
保留为“true”顶部类型
当A
是一个并集类型时,大部分的CheckIfExtends都会做一些有趣的事情,因为如果并集的片段满足两个分支,那么它允许使用条件的两个分支。当A
为any
时,其分布也相同,但B
为any
或未知
时除外(因此T8
表现正常)。有一些问题