交叉类型是将多个类型合并为一个类型,可以把多种类型叠加到一起成为一种类型 它包含了所需的所有类型的特性
interface Ant{
name:string;
weight:number;
}
interface Fly{
flyHeight:number;
speed:number
}
//少了任何就一个属性都会报错
const flyAnt:Ant&Fly={
name:'蚂蚁雅黑',
weight:0.2,
flyHeight:20,
speed:1
}
联合类型与交叉类型很有关系 但是使用上却完全不同
let stringOrNumber:string|number = 0
stringOrNumber = ""
再看下面的这个例子 start函数的参数类型既是Bird|Fish 那么在start函数中 想要直接调用的话,
只能调用Bird和Fish都具备的方法,否则编译会报错。
class Bird {
fly(){
console.log('bird flying');
}
lagEggs(){
console.log('bird layEggs');
}
}
class Fish{
swim(){
console.log("Fish swimmimg");
}
layEggs(){
console.log('fish layEggs');
}
}
const bird = new Bird()
const fish = new Fish()
function start(pet:Bird|Fish){
//可调用layEggs, 因为Bird 和Fish都有这个方法
pet.layEggs()
//会报错:Property 'swim' does not exist on type "Bird|Fish"
// pet.swim()
}
start(bird)
start(fish)
语法: T extends K
这里的extends 不是类、接口的继承 而是对于类型的判断和约束,意思是判断T能否赋值给K
可以在泛型中对传入的类型进行约束
const copy = (value:string|number):string|number =>value
//只能传入string或者number
copy(10)
//会报错Argumnets of type 'boolean' is not assignable to parameter of type "string|number"
copy(false)
也可以判断T是够可以赋值给U ,可以的话返回T,否则返回never
type Exclude= T extends U ? T :never
会遍历指定接口的key 或者是遍历联合类型
interface Person{
name:string,
age:number,
gender:number
}
//将T的所有属性转换为只读类型
type ReadOnlyType={
readonly [P in keyof T]:T
}
// type ReadonlyType = {
// readonly name: Person;
// readonly age: Person;
// readonly gender: Person;
// }
type ReadOnlyPerson = ReadOnlyType
语法:parameterName is Type
parameterName 必须是来自当前函数签名里的一个参数名
判断parameterName是否是Type类型
如果想要在start函数中 根据情况去调用Bird的fly方法和fish的swim方法
首先想到的可能是直接检查成员是否存在,然后进行调用
function start(pet:Bird|Fish){
//调用layEggs没问题 因为Bird或者Fish都有layEggs方法
pet.layEggs();
if((pet as Bird).fly){
(pet as Bird).fly()
}else if((pet as Fish).swim){
(pet as Fish).swim()
}
}
但是这样做,判断以及调用的时候都要进行类型转换,未免有些麻烦,可能会想到写个工具函数判断下
function isBird(bird:Bird | Fish):boolean{
return !!(bird as Bird).fly
}
function isFish(fish:Bird|Fish):boolean{
return !!(fish as Fish).swim
}
function start(pet:Bird|Fish){
//调用layEggs 没问题 因为Bird或者Fish都有layEggs方法
pet.layEggs()
if(isBird(pet)){
(pet as Bird).fly()
}else if(isFish(pet)){
(pet as Fish).swim()
}
}
看起来简洁了一点 但是调用方法的时候 还是要进行类型转换才可以,否则还是会报错,那有什么好的方法,能让我们判断完类型之后,就可以直接调用方法,不用再进行类型转换呢?
OK,肯定是有的,类型谓词is就派上用场了
function isBird(bird:Bird|Fish):bird is Bird{
return !!(bird as Bird).fly
}
function start(pet:Bird|Fish){
pet.layEggs()
if(isBird(pet)){
pet.fly()
}else{
pet.swim()
}
}
每当使用一些变量调用isFish时,TypeScript会将变量缩减为那个具体的类型,只要这个类型与变量的原始类型是兼容的。
TypeScript不仅知道在If分支里pet是Bird类型;它还清楚在else分支里,一定不是Bird类型,一定是Fish类型
可以用infer P来标记一个泛型,表示这个泛型是一个待推断的类型,并且可以直接使用
比如下面这个获取函数参数类型的例子:
type ParamType = T extends (param:infer P) =>any ? P:T;
type FunctionType = (value:number)=>boolean
type Param = ParamType; //type Param = number
type OtherParam = ParamType//type Param = symbol
判断T是否能够赋值给(Param:infer P)=>any,并且将参数推断为泛型P,如果可以赋值 则返回参数类型P, 否则返回传入的类型
语法:typeof v === “typename” 或 typeof v !== “typename”
用来判断数据的类型是否是某个原始类型(number,string,boolean,symbol)并进行类型保护
typename必须是number,string,boolean或symbol,但是TypeScript并不会阻止你与其他字符串比较,语言不会把那些表达式识别为类型保护。
看下面这个例子,print函数会根据参数类型打印不同的结果,那如何判断参数是string还是number呢
function print(value:number|string){
//如果是string类型
// console.log(value.split('').join(','))
//如果是number类型
//console.log(value.toFixed(2))
}
有两种常用判断方式:
1.根据是否包含split属性判断是string类型,是否包含toFixed方法判断是number类型
弊端:不论是判断还是调用都要进行类型转换
2.使用类型谓词is
弊端:每次都要去写一个工具函数 太麻烦了
用法:这就到了typeof一展身手的时候了
function print(value:number|string){
if(typeof value ==="string"){
console.log(value.split('').join(','));
}else{
console.log(value.toFixed(2));
}
}
使用typeof进行类型判断后,TypeScript会将变量缩减为那个具体的类型,只要这个类型与变量的类型是兼容的
与typeof类似,不过作用方式不同 instanceof类型保护是通过构造函数来细化类型的一种方式
instanceof的右侧要求是一个构造函数 TypeScript将细化为:
此构造函数的prototype属性的类型,如果它的类型不为any的话
构造签名所返回的类型的联合
还是以类型谓词is示例中的代码做演示:
最初代码:
function start(pet:Bird|Fish){
//调用layEggs没问题,因为Bird或者Fish都有layEggs方法
pet.layEggs()
if((pet as Bird).fly){
(pet as Bird).fly()
}else if((pet as Fish).swim){
(pet as Fish).swim()
}
}
使用instanceof 后的代码:
function start(pet:Bird|Fish){
pet.layEggs()
if(pet instanceof Bird){
pet.fly()
}else{
pet.swim()
}
}
可以达到相同的效果
语法:keyof T
对于任何类型 T,keyof T 的结果为T上已知的公共属性名的联合
interface Person{
name:string;
age:number
}
type PersonProps = keyof Person; //name|age
这里,keyof Person 返回的类型和name|age联合类型是一样,完全可以互相替换
//用法:keyof只能返回类型上已知的公共属性名
class Animal{
type:string;
weight:number;
private speed:number
}
type AnimalProps = keyof Animal;//type|weight
例如我们经常会获取对象的某个属性值,但是不确定是哪个属性,这个时候可以使用extends配合typeof 对属性名进行限制 限制传入的参数只能是对象的属性名
const person = {
name:'Jack',
age:20
}
function getPersonValue(fieldName: keyof typeof person){
return person[fieldName]
}
const nameValue = getPersonValue('name')
const ageValue = getPersonValue('age')
//会报错:Argument of type "gender" is not assignable to parameter of type "name"|"age"
// getPersonValue('gender')
语法:T[K]
类似于js中使用对象索引的方式 只不过js中返回对象属性的值,而在ts中返回的是T对应属性P的类型
用法
interface Person{
name:string
age:number
weight:number|string
gender:'man'|'women'
}
type NameType = Person['name'] //string
type WeightType = Person['weight'] //string|number
type GenderType = Person['gender'] //'man'|'women'
###只读类型【Readonly】
定义:
type Readonly = {
readonly [P in keyof T]:T[P]
}
用于将T类型的所有属性设置为只读状态
用法:
interface Person{
name:string
age:number
}
const person: Readonly = {
name:'Lucy',
age:22
}
//会报错:Cannot assign to "name" because it is a read-only property
// person.name = "Lily"
readonly 只读 被readonly标记的属性只能声明时或类的构造函数中赋值 之后将不可改(即只读属性)
定义
interface ReadonlyArray{
// Iterator of values in the array
[Symbol.iterator]():IterableIterator;
// Return an iterable of keyof,value pairs for every entry in the Array
entries():IterableIterator<[number,T]>;
// Returns an iterable of keys in the Array
keys():IterableIterator;
// Return an iterable of values in the Array
values():IterableIterator
}
只能在数组初始时为变量赋值,之后数组无法修改
使用
interface Person{
name:string
}
const personList:ReadonlyArray = [{name:"Jack"},{name:'Rose'}]
//会报错:Property "push" does not exist on type 'readonly Person[]'
// personList.push({name:'Lucy'})
//但是内部元素如果是引用类型 元素自身是可以进行修改的
personList[0].name = 'Lily'
用于将T类型的所有属性设置为可选状态,首先通过keyof T,取出类型T的所有属性,然后通过in操作符进行遍历,最后在属性后加上?,将属性变为可选属性
定义
type Partial = {
[P in keyof T]?:T[P];
}
用法
interface Person{
name:string
age:number
}
//会报错:Type "{}" is missing the following properties from type "Person":name,age
//let person:Person = {}
//使用Partial 隐射后返回的新类型,name和age都变成了可选属性
let person:Partial={}
person = {name:"pengzu",age:800}
person = {name:'z'}
person = {age:18}
和Partial的作用相反
用于将T类型的所有属性设置为必选属性 首先通过keyof T,取出类型T的所有属性,然后通过in操作符进行遍历,最后在属性后的?前加上-,将属性变为必选属性
定义
type Required{
[P in keyof T]-?:T[P]
}
// 使用
interface Person{
name?:string
age?:number
}
// 使用Required 隐射后返回的新类型 name和age都变成了必选属性
//会报错:Type "{}" is missing the following properties from type "Required":name,age
let person:Required = {}
定义
type Pick={
[P in K]:T[P]
}
从T类型中提取部分属性 作为新的返回类型
使用:比如我们在发送网络请求时,只需要传递类型中的部分属性,就可以通过Pick来实现
interface Goods{
type:string
goodsName:string
price:number
}
//作为网络请求参数 只需要goodsName和price就可以
type RequestGoodsParams = Pick
//返回类型
//type RequestGoodsParams = {
//goodsName:string;
//price:number
// }
const params:RequestGoodsParams= {
goodsName:'',
price:10
}
定义:type Omit
和Pick作用相反,用于从T类型中 排除部分属性
用法比如长方体有长宽高,而正方体长宽高相等,所以只需要长就可以了,那么此时就可以使用Omit来生成正方体的类型
interface Reactanglar{
length:number;
height:number;
width:number;
}
type Square = Omit
// 返回类型
// type Square = {
// length:number
// }
const temp:Square = {length:5}
语法:Extract
提取T中可以赋值给U的类型
定义:
type Extract = T extends U?T:never
用法:
type T01 = Extract<'a'|'b'|'c'|'d','a','c','f'> //a|c
type T02 = Extractvoid),Function>//()=>void
语法:Exclude
与Extract用法相反 从T中剔除可以赋值给U的类型
定义:type Exclude
用法:
type T00 = Exclude<'a'|'b'|'c'|'d','a'|'c'|'f'>//'b'|'d'
type T01 = Excludevoid),Function> //string|number
定义:
type Record={
[P in K]:T
}
接收两个泛型,K必须可以是可以赋值给string|number|symbol的类型,通过in 操作符对K进行遍历,每一个属性的类型都必须是T类型
**用法:比如我们想要将Person类型的数组转化成对象映射,可以使用Record来指定隐射对象的类型
在这里插入代码片
interface Person{
name:string
age:number
}
const personList = [
{name:'jack',age:25},
{name:'lucy',age:22},
{name:'rose',age:18}
]
const personMap:Record = {}
personList.map((person)=>{
personMap[person.name] = person
})
比如在传递参数时,希望参数是一个对象,但是不确定具体的类型,就可以使用Record作为参数类型
function doSomething(obj:Record){}
定义:type NonNullable = T extends null | undefined?never:T
从T中剔除null、undefined、never类型 不会剔除void,unkown类型
type T01 = NonNullable//string|number
type T02 = NonNullable<(()=>string),string[]|null|undefined>//(()=>string)}|string[]
type T03 = NonNullable<{name?:string,age:number}|string[]|null|undefined>//{name?:string,age:number}|string[]
返回class 中构造函数参数类型组成的元组类型
// 定义
// Obtain the parameters of a constructor function type in a tuple
type ConstructorParametersany> = T extends new (...args:infer P)=>any?P:never;
// 使用
class Person{
name:string
age:number
gender:'man'|'women'
constructor(name:string,age:number,gender:'man'|'women'){
this.name = name
this.age = age
this.gender = gender
}
}
type ConstructorType = ConstructorParameters//[name:string,age:number,gender:'man'|'women']
const params:ConstructorType=['jack',12,'man']
获取class构造函数的返回类型
定义:
// 使用
class Person{
name:string
age:number
weight:number
gender:'man'|'women'
constructor(name:string,age:number,gender:'man'|'women'){
this.name = name
this.age = age
this.gender = gender
}
}
type Instance = InstanceType//person
const params:Instance={
name:'jack',
age:22,
weight:120,
gender:'man'
}
获取函数的参数类型组成的元组
// Obtain the return type of a function type in a tuple
type InstanceTypeany>=T extends new (...args:any)=>infer R?R:any;
// 用法:
type FunctionType = (name:string,age:number)=>boolean
type FunctionParamsType = Parameters//[name:string,age:number]
const params:FunctionParamsType=['Jack',20]
获取函数的返回值类型
定义
// Obtain the return type of a function type
type ReturnTypeany>= T extends (...args:any)=>infer R?R:any;
// 使用
type FunctionType = (name:string,age:number)=>boolean|string
type FunctionReturnType = ReturnType //boolean|string
高级类型
关键字
映射类型