switch不能够作⽤在string类型上_⾯向Type编程--Typescript
类型和。。。
简述
前⾯我们讨论了Typescript常⽤类型操作符typeof、keyof、in、extends、infer等。合理的使⽤这些类型操作符,我们创建很多实⽤的类型和类型⼯具。总结归纳:
typeof提供了对象转类型的⽅法和途径
keyof提供了获取类型属性键值的能⼒
in提供遍历操作能⼒
extends提供了范围限制和条件判断能⼒
infer结合extends提供了声明特定位置待推断类型的能⼒
总⽽⾔之,合理结合使⽤泛型函数、类型和操作符,使我们具备了类型编程的能⼒。下⾯我们⾸先详细的讨论⼀下常⽤的⼀些⾼级类型以及泛型函数,进⽽探讨⼀下类型编程技巧,最后再⼀起认识和解析⼀
些常⽤的类型函数和⼯具。希望通过讨论,⼤家能够熟练的掌握Typescript类型和类型操作符,具备类型编程能⼒。
⼀、字⾯量类型
什么是字⾯量类型
字⾯量也叫直接量。计算机科学中,字⾯量(literal)是⽤于表达源代码中⼀个固定值的表⽰法(notation)。字⾯量类型是固定值表⽰的类型。通常我们使⽤的string,number,boolean等类型属于集合类型,例如string是所有字符串集合。字⾯量类型不同于集合类型,它只有⼀个类型实例,即其固定值,所以字⾯量类型也叫单位类型(Unit Type)
字⾯量类型分类
字符串字⾯量类型(String Literal Types)
数字字⾯量类型(Number Literal Types)
布尔字⾯量类型(Boolean Literal Types)
枚举字⾯量类型(Enum Literal Types)
字符串字⾯量类型(String Literal Types)
letfoo:'Hello'foo='Hello'// ok
foo='Bar';// Error: 'Bar' 不能赋值给类型 'Hello'
typeCF=typeoffoo// type CF = "Hello"
constfoo1='World'typeCF1=typeoffoo1// type CF1 = "World"
字⾯量联合类型
字⾯量联合类型 单纯的字⾯量类型并不是很实⽤,更多的场景是联合类型的形式出现,⽤于有限的、有特定关联的固定值类型。例如四季名称、星期、⽅向名称、⾊⼦的六个⾯等等。类型'Hello'不同于其实例值'Hello',也不同于string类型的实例‘Hello’
typeCardinalDirection='North'|'East'|'South'|'West';functionmove(distance:number,direction:CardinalDirection){// ...
}move(1,'North');// ok
move(1,'Nurth');// Error
字⾯量类型与keyof 前⾯讲过,结合keyof可以获取类型属性键值的字⾯量类型的联合类型
字⾯量类型与keyof
interfacePerson{name:string;age:number;location:string;}typeK1=keyofPerson;// "name" | "age" | "location"
推断类型陷阱。
推断类型陷阱。 虽然这⾥使⽤了const声明变量,但是变量test的类型是{someProp:string},所以属性someProp被推断为string类型。string类型的实例'foo'⽆法分配给字⾯量类型'foo'
functioniTakeFoo(foo:'foo'){}consttest={someProp:'foo'};iTakeFoo(test.someProp)// 类型“string”的参数不能赋给类型“"foo"”的参数
可以使⽤类型断⾔解决这个问题。下⾯声明常量test的时,someProp属性使⽤了类型断⾔。因此test的声明类型被推断为
{someProp:'foo'},此处的'foo'是字⾯量类型,满⾜函数iTakeFoo的参数类型要求
functioniTakeFoo(foo:'foo'){}consttest={someProp:'foo'as'foo'};iTakeFoo(test.someProp);// ok
数字字⾯量类型(Number Literal Types)
数字字⾯量类型,⼤致跟上⾯的字符串字⾯量类型相同,可以把具体固定的值当类型使⽤。同时,也要注意推断类型陷阱的问题。
letzeroOrOne:0|1;zeroOrOne=0;// OK
zeroOrOne=1;// OK
zeroOrOne=2;// Error: Type '2' is not assignable to type '0 | 1'
functiongetAge(age:28){}constperson={age:28}getAge(person.age)// 类型“number”的参数不能赋给类型“28”的参数
typeTN=typeofzeroOrOne// TN = 0 | 1
应⽤实例 下⾯这个例⼦应⽤场景很常见,这是⼀个处理端⼝号的函数,返回值是数字字⾯量类型组成的联合类型。
应⽤实例
functiongetPort(scheme:"http"|"https"):80|443{switch(scheme){case"http":return80;case"https":return443;}}consthttpPort=getPort("http");// Type 80 | 443函数重载的影响
函数重载的影响 但是结合Typescript的函数重载⼀起使⽤,返回值就明确多了。
functiongetPort(scheme:"http"):80;functiongetPort(scheme:"https"):443;functiongetPort(scheme:"http"|"https"):80|443{switch(scheme){case"http":return80 consthttpsPort=getPort("https");// Type 443
枚举字⾯量类型(Enum Literal Types)
枚举类型同样也可以⽤作字⾯量类型。继续上⾯的例⼦,我们先声明⼀个包含两个端⼝号的枚举常量
constenumHttpPort{Http=80,Https=443}
同样利⽤函数重载,不过这次我们创建⼀个getScheme函数
functiongetScheme(port:HttpPort.Http):"http";functiongetScheme(port:HttpPort.Https):"https";functiongetScheme(port:HttpPort):"http"|"https"{switch(port){枚举常量类型编译规则
枚举常量类型编译规则 枚举常量没有运⾏时表现形式(除⾮你设置了preserveConstEnums编译选项),编译器将会直接编译成相应的值,⽽不是变量。看下⾯编译结果,和s编译成了80和443
这也是提⾼代码性能的⼩技巧
functiongetScheme(port){switch(port){case80:return"http";case443:return"https";}}varscheme=getScheme(80);
⼆、never
never类型定义 never是Typescript类型中的底部类型。底部类型是没有值的类型,也称为零类型或者空类型。底部类型是所有类型never类型定义
的⼦类,⽤符号表⽰是(⊥)。
switch case判断字符串never应⽤场景 通常下⾯两种情况会⽤到never类型:
never应⽤场景
⽤于表⽰不会有返回值的函数的返回类型:例如,永远循环的函数,始终抛出异常信号的函数等
类型变量受永不可能为真的条件限制,由于类型保护机制,变量类型收窄为never类型
不会有返回值的函数
这是⼀⽆限循环函数,没有任何的终⽌循环语句,函数执⾏永远不会结束,因此不会有任何的返回值。
constsing=function(){while(true){console.log("Never gonna give you up");console.log("Never gonna let you down");console.log("Never gonna run around and dese 下⾯是⼀个始终抛出异常的函数
constfailwith=(message:string)=>{thrownewError(message);}
在⼀个永远⽆法为真的逻辑判断中,类型会收窄为never
functioncontrolFlowAnalysisWithNever(value:string|number){if(typeofvalue==="string"){value;// Type string
}elseif(typeofvalue==="number"){value;// Type number
}else{value;// Type never
}}
never类型的特点
never是任意类型的⼦类型,并且可以分配给任意类型
letn:neverleta:string=nletb:number=nletc:boolean=nletd:'d'=nlete:never=nletf:()=>void=nletg:unknown=nleth:any=nleti:symbol=n
1. never没有⼦类型,并且除了never本⾝,没有类型可以分配给never类型
letn:neverletn1:never=n// ok
n='s'// 不能将类型“string”分配给类型“never”
n=1// 不能将类型“number”分配给类型“never”
n=false// 不能将类型“boolean”分配给类型“never”
n=(...arg:any)=>void;// 不能将类型“(...arg: any) => any”分配给类型“never”
n=newArray()// 不能将类型“any[]”分配给类型“never”
n=Symbol()// 不能将类型“symbol”分配给类型“never”
n={}// 不能将类型“{}”分配给类型“never”
1. 在⼀个没有返回值标注的函数表达式或箭头函数中, 如果函数没有 return 语句, 或者仅有表达式类型为 never 的 return 语句, 并且函
数的终⽌点⽆法被执⾏到 (按照控制流分析), 则推导出的函数返回值类型是 never
// Return type: never
constfailwith1=function(message:string){thrownewError(message);}// Return type: never
constfailwith2=(message:string)=>{thrownewError(message);}
1. 这种规则并不适⽤于函数声明,这么做的原因为了向后兼容。因此,最合理的⽅式是明确的声明返回类型
// Return type: void
functionfailwith3(message:string){thrownewError(message);}
1. 在⼀个明确指定了 never 返回值类型的函数中, 所有 return 语句 (如果有) 表达式的值必须为 never 类型, 且函数不应能执⾏到终⽌
// 报错, 返回“never”的函数不能具有可访问的终结点
functiontypeWithNever(arg:string|number):never{letrst:neverletrst1:stringletrst2:numberif(typeofarg==='string'){returnrst1// 报错,不能将类型“string”分配给类型“ne }elseif(typeofarg==='number'){returnrst2// 报错,不能将类型“number”分配给类型“never”
}}
使函数⽆法执⾏到终⽌点的⽅式有很多种,⽐如,创建永远都⽆法执⾏的条件分⽀;增加⽆限循环语句;抛出异常等 另外,每个分⽀返回
类型必须是never,包括永远⽆法执⾏的分⽀
functiontypeWithNever(arg:string|number):never{letrst:neverif(typeofarg==='string'){returnrst}elseif(typeofarg==='number'){returnrst}else{returnrst}}// or functiontypeWithNever(arg:string
|number):never{letrst:neverletrst1:stringletrst2:numberif(typeofarg==='string'){returnrst}elseif(typeofarg==='number'){return functiontypeWithNever(arg:string|number):never{letrst:neverletrst1:stringletrst2:numberif(typeofarg==='string'){returnrst}elseif(typeofarg==='number'){return
never类型是底部类型,是任意类型的⼦类型,所以任意类型和never的交叉类型都是never。
typeT1=number&never;// never
typeT2=string&never;// never
typeT3='a'&never;// never
typeT4=1&never;// never
typeT5=true&never;// never
typeT6=any&never;// never
typeT7=unknown&never;// never
typeT8=never&never;// never
可以分配给never的只有never,⽽且never可以分配给其他任何类型。所以联合never是没有意义的。就⾏+0跟没有添结果是⼀样的
typeT1=number|never;// number
typeT2=string|never;// string
typeT3='a'|never;// 'a'
typeT4=1|never;// 1
typeT5=true|never;// true
typeT6=any|never;// any
typeT7=unknown|never;// unknown
typeT8=never|never;// never
利⽤这个特性可以做很多事情,⽐如筛选。先把利⽤条件类型需要过滤的类型转换成never,然后利⽤联合类型过滤掉never
typeFilter<T,U>=TextendsU?T:nevertypeR=Filter<'a'|2|false|'b',string>// R = 'a'|'b'
按照分布式条件类型机制,Filter<'a' | 2 | false | 'b', string>将被分解成如下代码
typeR=|('a'extemdsstring?'a':never)|(2extendsstring?2:never)|(falseextendsstring?false:never)|('b'extendsstring?'b':never)
进⼀步将被解析,最终得到 'a' | 'b'
typeR='a'|never|never|'b'typeR='a'|'b'
三、unknown类型
定义
定义 unknown是所有类型的⽗类型,任何类型都是unknown的⼦类型。跟never类型相对的,unknown是顶部类型(Top Type),符号是(⊤)
特点
任何类型的实例都可以分配给unknown
leta:unknowna=Symbol('deep dark fantasy')a={}a=falsea='114514'a=1919n
1. unknown只能分配给unknown,不能分配给其他任何类型
leta:unknownletb:string=a// 不能将类型“unknown”分配给类型“string”
letc:number=a// 不能将类型“unknown”分配给类型“number”
letd:boolean=a// 不能将类型“unknown”分配给类型“boolean”
lete:()=>void=a// 不能将类型“unknown”分配给类型“() => void”
letf:symbol=a// 不能将类型“unknown”分配给类型“symbol”
letg:unknown=a// ok
1. 任意类型和unknown的交叉类型都是其本⾝
typeT=string&unknown// string
typeT1=number&unknown// number
typeT2=boolean&unknown// boolean
typeT3=symbol&unknown// symbol
typeT4=never&unknown// never
typeT5=any&unknown// any
typeT6='a'&unknown// 'a'
typeT7=1&unknown// 1
typeT8=false&unknown// false
typeT9=string[]&unknown// string[]
1. 任意类型和unknown的联合类型都是unknown
typeT=string|unknown// unknown
typeT1=number|unknown// unknown
typeT2=boolean|unknown// unknown
typeT3=symbol|unknown// unknown
typeT4=never|unknown// unknown
typeT5=any|unknown// unknown
typeT6='a'|unknown// unknown
typeT7=1|unknown// unknown
typeT8=false|unknown// unknown
typeT9=string[]|unknown// unknown
四、交叉类型(Intersection Types)
定义
定义 交叉类型⽤&操作符把⼏个类型的成员合并,形成⼀个拥有这⼏个类型所有成员的新类型。
声明交叉类型
需要注意的是,不能从字⾯上理解交叉类型,它不是⼏个类型的交集,⽽是具备所有类型成员的新类型。可以把操作符&理解成and,A & B 表⽰同时包含 A 和 B 的所有成员,我们可以直接使⽤它,⽽不需要判断是否存在该属性
interfaceIPerson{name:string;age:number;}interfaceIStudent{grade:number;}typeIIT=IPerson&IStudent/** IIT = {
name: string;
age: number;
grade: number;
}*/letuser:IIT={name:'Joi',age:12,grade:6}

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。