You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
functionprintAll(strs: string|string[]|null){if(typeofstrs==="object"){for(constsofstrs){// Object is possibly 'null'.console.log(s);}}elseif(typeofstrs==="string"){console.log(strs);}else{// do nothing}}
functionexample(x: string|number,y: string|boolean){if(x===y){// We can now call any 'string' method on 'x' or 'y'.x.toUpperCase();y.toLowerCase();}else{console.log(x);// (parameter) x: string | numberconsole.log(y);// (parameter) y: string | boolean}}
以上代码中,当我们确保 x 和 y 值和类型都相同时,Typescript 会发现它们只有一个公用类型 string,所以在此分支中,可以直接使用任意字符串方法
interfaceContainer{value: number|null|undefined;}functionmultiplyValue(container: Container,factor: number){// Remove both 'null' and 'undefined' from the type.if(container.value!=null){console.log(container.value);// (property) Container.value: number// Now we can safely multiply 'container.value'.container.value*=factor;}}
The in operator narrowing
在 Javascript 中,可以通过 in 操作符来判断某个属性是否存在一个对象中。Typescript 可以通过这种方式来 narrowing:
letx=Math.random()<0.5 ? 10 : "hello world!";// let x: string | numberx=1;console.log(x);// let x: numberx="goodbye!";console.log(x);// let x: stringx=true// Type 'boolean' is not assignable to type 'string | number'.
这里需要注意,尽管在 x = 1 这个赋值表达式之后,可以观察到 x 的类型为 number ,但仍旧可以将 string 类型的值赋予 x 。这是因为 x 的声明类型(即最初的类型)是 string | number
functionexample(){letx: string|number|boolean;x=Math.random()<0.5;console.log(x);// let x: booleanif(Math.random()<0.5){x="hello";console.log(x);// let x: string}else{x=100;console.log(x);// let x: number}returnx;// let x: string | number}
想要定义一个自定义 type guard,仅需要简单的定义一个返回值是 type predicate 的函数:
functionisFish(pet: Fish|Bird): pet is Fish{return(petasFish).swim!==undefined;}
此例中,pet is Fish 就是 type predicate(类型谓语?) ,类型谓语总是 parameterName is Type 这种形式( parameterName 必须存在于当前函数签名中)
任何时候,当 isFish 被调用,Typescript 都可以进行 narrowing:
letpet=getSmallPet();// let pet: Fish | Birdif(isFish(pet)){pet.swim();// let pet: Fish}else{pet.fly();// let pet: Bird}
也可以通过类型守卫 isFish 来过滤一个 Fish | Bird 数组,获得一个 Fish 数组:
constzoo: (Fish|Bird)[]=[getSmallPet(),getSmallPet(),getSmallPet()];constunderWater1: Fish[]=zoo.filter(isFish);// or, equivalentlyconstunderWater2: Fish[]=zoo.filter(isFish)asFish[];// The predicate may need repeating for more complex examplesconstunderWater3: Fish[]=zoo.filter((pet): pet is Fish=>{if(pet.name==="sharkey")returnfalse;returnisFish(pet);});
functiongetArea(shape: Shape){returnMath.PI*shape.radius**2;// Property 'radius' does not exist on type 'Shape'.// Property 'radius' does not exist on type 'Square'.}
interfaceTriangle{kind: "triangle";sideLength: number;}typeShape=Circle|Square|Triangle;functiongetArea(shape: Shape){switch(shape.kind){case"circle":
returnMath.PI*shape.radius**2;case"square":
returnshape.sideLength**2;default:
const_exhaustiveCheck: never=shape;// Type 'Triangle' is not assignable to type 'never'.return_exhaustiveCheck;}}
The text was updated successfully, but these errors were encountered:
想象下有个函数
padLeft
,它要实现的功能是:如果参数padding
是number
类型,表示会在参数input
前增加多少个空格。如果参数padding
是string
类型,直接加在参数input
前:Typescript 报错,并提示
number
类型和number | string
类型相加可能不是我们想要的结果。因此需要在使用前显式的检测参数padding
是不是number
类型:Typescript 会分析运行时代码并和静态类型结合:
if/else
结构的控制流、三目表达式、循环等等。在上面的例子中,Typescript 在if
语句中看到typeof padding === "number"
并了解到这是一种叫type guard
的特殊形式代码,并根据程序可能的执行路径分析出一个更具体的类型。类似这种特殊的检查被称为Narrowing
typeof
type guards在 Javascript 中,
typeof
操作符会在运行时返回操作值的基本类型信息字符串:string
number
bigint
boolean
symbol
undefined
object
function
Typescript 甚至兼容了许多 Javascript 的“怪癖”,例如对
null
使用typeof
操作符会返回object
:在这个例子中,
strs
仅仅被narrow
至类型string[] | null
而不是string[]
,因此 Typescript 会报错Truthiness narrowing
在 Javascript 中,可以在条件判断语句(
if
&&
||
!
)中使用任意表达式,例如if
语句并不要求括号中必须是boolean
类型,它会将括号中的值/语句强制类型转化为boolean
类型的值去做判断。以下这些值会被转换为false
:0
NaN
""
0n(the bigint version of zero)
null
undefined
而其他值都会被转换为
true
。始终可以通过Boolean
函数或者双重否定表达式!!
来将某个值转化为boolean
类型的值(需要注意的是,前者在 Typescript 中会被类型推论为boolean
,而后者会被推论为一个字面量布尔类型true
?)修改上面的
printAll
函数,加入truthiness narrowing
,会发现 Typescript 的报错消失了:需要记住,对基本类型做真实性检查通常会引发错误,考虑以下代码:
这里用一个真实性检查包裹了整个函数体,但是有一个微妙的问题:忽略了处理空字符串场景
对于不熟悉 Javascript 的人来说,这是需要注意的情况。Typescript 可以帮助我们更早的捕获错误,但是如果我们对某个值不做任何操作,它仅能在不过度规定的情况下做这么多(没看懂,个人理解是处理不了这种情况)。如果需要,可以使用类似
linter
这样的工具最后一点关于
Truthiness narrowing
,布尔否定!
时,Typescript 会从否定分支进行narrowing
:Equality narrowing
Typescript 同样会使用
switch
语句,相等检查例如===
!==
==
!=
来进行narrowing
:以上代码中,当我们确保
x
和y
值和类型都相同时,Typescript 会发现它们只有一个公用类型string
,所以在此分支中,可以直接使用任意字符串方法检查是否特定类型的字面量也是生效的,考虑如下代码,通过特定检查,Typescript 正确的将
null
类型从strs
上过滤掉了:Javascript 的宽松相等
==
!=
在 Typescript 中也可以正确的narrowing
。在宽松相等中,检查某个值是否null
不仅仅是特定值null
本身,同样也检查是否是undefined
,反之亦然(不管怎么说还是不要用宽松相等了...):The
in
operator narrowing在 Javascript 中,可以通过
in
操作符来判断某个属性是否存在一个对象中。Typescript 可以通过这种方式来narrowing
:需要注意的是,可选属性在
narrowing
时,会同时存在于两边:instanceof
narrowing在 Javascript 中,
x instanceof Foo
可以检查Foo.prototype
是否存在于x
的原型链中。同样的,Typescript 中可以通过instanceof
来narrowing
:Assignments
当我们给一个变量赋值时,Typescript 会分析赋值表达式右侧,并
narrowing
合适的左侧类型:这里需要注意,尽管在
x = 1
这个赋值表达式之后,可以观察到x
的类型为number
,但仍旧可以将string
类型的值赋予x
。这是因为x
的声明类型(即最初的类型)是string | number
Control flow analysis
基于“流程上是否可到达”的分析也被称作控制流分析。Typescript 会根据控制流
narrowing
类型 — 当变量遇到类型守卫或赋值表达式。当一个变量被分析时,控制流可能一次又一次的拆分或重新合并,在每一个拆分点或合并点,变量都会被观测到不同的类型:Using type predicates
截至目前,我们已经通过 Javascript 的结构来处理
narrowing
,但是有时候我们可能想要更直接的在代码中操作类型变化想要定义一个自定义
type guard
,仅需要简单的定义一个返回值是type predicate
的函数:此例中,
pet is Fish
就是type predicate(类型谓语?)
,类型谓语总是parameterName is Type
这种形式(parameterName
必须存在于当前函数签名中)任何时候,当
isFish
被调用,Typescript 都可以进行narrowing
:也可以通过类型守卫
isFish
来过滤一个Fish | Bird
数组,获得一个Fish
数组:Discriminated unions
思考如下代码,定义一个
Shape
接口,kind
字段是一个联合字面量类型(防止拼写错误)。同时,当图形为圆时,我们关注的是radius
字段,当图形为正方形时,则是sideLength
,因此这两个字段是可选的:当我们想要编写一个
getArea
函数(基于是圆形还是正方形计算面积):如果设置了
strictNullChecks
配置项,会获得一个错误,radius
可能是没有定义的。如果我们尝试通过kind
字段来优化这个问题呢?Typescript 依旧不会理解这里到底要做什么。在此处,我们对于类型的了解比 Typescript 要多,可以尝试用
non-null
断言告诉 Typescriptradius
一定存在:但是这种方式仍然不理想且容易出错。另外,如果不设置
strictNullChecks
,虽然可以随意访问任何属性,但是也同样极容易导致错误(可选属性会被假设为始终存在)问题在于类型检查器没有任何途径根据
kind
属性知道radius
或sideLength
是否存在,考虑到这个问题,可以换一种方式来定义Shape
:这里,我们拆分了
Shape
变成两个类型,同时radius
和sideLength
不再是可选属性,而是必选属性。此时如果访问Shape
类型的radius
属性:当我们使用可选属性时,Typescript 仅能警告这条属性可能不存在。当联合两个接口时,Typescript 会告诉我们
Shape
有可能是Square
,而Square
上不存在radius
属性!与此同时,如果再次尝试检查
kind
属性:错误消失了!当一个联合类型中的所有成员都有同一个属性时(此属性必须是字面量类型),Typescript 会认为这是一个
discriminated union
,并可以根据此属性来narrow
联合类型的成员,此例中kind
会被认为是Shape
的“区别属性”。同样的,switch
语句也可以正常生效:The never type
如果我们通过
narrowing
,在某个点排除了所有可能的类型,那么此时,Typescript 会使用never
类型来表示这种不应该不存在的状态Exhaustiveness checking
never
类型可以赋予任何类型,但是任何类型都不能赋予never
类型(除了never
本身),可以依靠这个特性,在switch
语句中做一个是否穷尽判断:如果在此时给联合类型新增一个成员,Typescript 会报错:
The text was updated successfully, but these errors were encountered: