1、交叉类型(&)
交叉类型(&):功能类似于接口继承(extends),用于组合多个类型为一个类型(常用于对象类型)
代码示例:
interface Person {
name: string
say():number
}
interface Contact{
phone: string
}
type PersonDetail = Person & Contact
let obj: PersonDetail = {
name: '张三',
phone: '13812345678',
say() {
return 1
},
}
交叉类型和接口继承的对比:
相同点:都可以实现对象类型的组合
不同点:两种方式实现类型组合时,对于同名属性之间,处理类型冲突的方式不同。接口继承会报错(类型不兼容);交叉类型不会报错,会兼容
代码示例:
//接口继承会报错
// interface A{
// fn:(value: number)=>string
// }
// interface B extends A{
// fn:(value: string)=>string
// }
//交叉类型不会报错
interface A{
fn:(value: number)=>string
}
interface B{
fn:(value: string)=>string
}
type C = A & B
let p: C = {
fn(value: number | string) {
return ''
}
}
2、泛型
泛型是可以在保证类型安全的前提下,让函数与多种类型一起工作,从而实现复用,常用于:函数、接口、class中。当想要接受多种不同的类型的时候可以使用泛型。
代码示例:
function id<Type>(value: Type): Type {
return value
}
id<number>(1)
id('a')
解释:
1、语法:在函数名称的后面添加<>(尖括号),尖括号中指定具体的类型
2、当传入类型number后,这个类型就会被函数声明时指定的类型变量Type捕获到。
3、此时,Type的类型就是number,所以,函数id参数和返回值的类型也都是number。
4、Type是自己定义的名称,也可以叫其他名称。
5、在调用泛型函数时,可以省略<类型>来简化泛型函数的调用,TS内部会采用类型参数推断的机制来推断出Type的类型。
6、当编译器无法推断类型或推断的类型不准确时,就需要显示的传入类型参数
泛型约束
1、指定更加具体的类型
2、添加约束
代码示例:
//指定更加具体的类型
function id<Type>(value: Type[]): Type[] {
console.log(value.length);
return value
}
id<number>([1])
//添加约束
interface ILength {
length: number
}
function id2<Type extends ILength>(value: Type): Type{
value.length
return value
}
id2({ length: 7, name: 's' })
id2([1,22,'w'])
添加约束
通过extends关键字使用接口,为泛型添加约束
该约束表示:传入的类型必须具有length属性,传入的实参只要有length属性即可
泛型的类型变量可以有多个,并且类型变量之间还可以约束
代码示例:
function getProp<Type, Key extends keyof Type>(obj: Type, key: Key) {
return obj[key]
}
getProp([1, 2, 3], 1)
getProp('abc', 'split')
getProp('abc',10)//此处的10表示索引
console.log(getProp({name:'a',age:19},'age'));
解释:
1、添加了第二个类型变量key,两个类型变量之间使用(,)逗号分隔。
2、keyof关键字接受一个对象类型,生成其键名称(可能是字符串或数字)的联合类型。
3、上面示例中第四个函数调用中的keyof Type实际上获取的是对象所有键的联合类型,也就是:'name'|'age'
4、类型变量Key受Type约束,Key只能是Type所有键中的任意一个,或者说只能访问对象中存在的属性。
5、Key名称可以自己定义成任何符合的变量名称
泛型接口
接口也是可以配合泛型来使用,以增强其灵活性,增强复用性
代码示例:
interface IdFunc<Type>{
id: (value: Type) => Type
ids: () => Type[]
}
let obj2: IdFunc<number> = {
id(value) {
return value
},
ids() {
return [1,2]
},
}
解释:
1、在接口名称的后面添加<类型变量>,那么,这个接口就变成了泛型接口。
2、接口中的类型变量,对接口中所有其他成员可见,也就是接口中所有成员都可以使用类型变量。
3、使用泛型接口时,需要显示指定具体的类型。
实际上,JS中的数组在TS中就是一个泛型接口
当我们在使用数组时(如map、forEach等),TS会根据数组的不同类型,来自动将类型变量设置为相应的类型
可以通过Ctrl+鼠标左键来查看具体的类型信息
泛型类
class也可以配合泛型来使用
代码示例:
class GenericNumber<NumType>{
defaultValue: NumType
add: (x: NumType, y: NumType) => NumType
constructor(value: NumType) {
this.defaultValue = value
}
}
const myNum = new GenericNumber<number>(122)//可以省略<类型>不写
myNum.defaultValue = 10
解释:
1、类似于泛型接口,在class名称后面添加<类型变量>,这个类就变成了泛型类
2、类似于泛型接口,在创建class实例时,在类名后面通过<类型>来指定明确的类型
3、此处的add方法,采用的是箭头函数形式的类型书写方式
泛型工具类型
TS内置了一些常用的工具类型,他们都是基于泛型实现的
主要有:
1、Partial<Type>:将Type的所有属性设置为可选,不会改变传入类型的本身,而是创建一个新的类型。
2、Readonly<Type>:将Type的所有属性都设置为只读
3、Pick<Type, Keys>:从Type中选择一组属性来构造新类型,第二个变量传入的属性必须是第一个类型变量中存在的属性。
4、Record<Keys,Type>:构造一个对象类型,属性键为Keys表示对象有哪些属性,属性类型为Type表示对象属性的类型,
代码示例:
interface Props {
id: string
children: number[]
title: string
}
type PartialProps = Partial<Props>
type pickProps = Pick<Props, 'id' | 'title'>
type RecordObj = Record<'a' | 'b' | 'c', string[]>
let c: RecordObj = {
a: ['a'],
b: ['1'],
c: ['c','3']
}
3、索引签名类型
使用场景:无法确定对象中有哪些属性(或者说对象中可以出现任意多个属性)
代码示例:
interface AnyObj {
[key: string]: number
}
let obj: AnyObj = {
age:19
}
解释:
1、使用[key: string]来约束该接口中允许出现的属性名称。表示只要是string类型的属性名称,都可以出现在对象中。
2、key只是一个占位符,可以换成任意合法的变量名称
3、JS中对象{}里的键是string类型的
在数组对应的泛型接口中,也可以用到索引签名类型
代码示例:
interface MyArray<T> {
[index: number]: T
}
let n: MyArray<number> = [1,2]
解释:
只要是number类型的键(索引)都可以出现在数组中,同时也要符合数组索引是number类型这一前提
4、映射类型
映射类型:基于旧类型创建新类型(对象类型),减少重复、提升开发效率
代码示例:
type PropKeys = 'x' | 'y' | 'z'
type Type2 = { x: number; y: number; z: number }
type Type1 = { [key in PropKeys]: number }
let s: Type1 = {
x: 1,
y: 2,
z: 3
}
解释:
1、映射类型是基于索引签名类型的,所以,该语法也使用了[]
2、key in PropKeys表示key可以是PropKeys联合类型中的任意一个,类似于forin(let k in obj)
3、Type2和Type1结构完全相同
4、注意:映射类型只能在类型别名中使用,不能在接口中使用
映射类型还可以根据对象类型来创建
代码示例:
type Props = { a: number; b: string; c: boolean }
type Type3 = { [key in keyof Props]: number }
let t: Type3 = {
a: 1,
b: 2,
c: 3
}
解释:
1、先执行keyof Props获取到对象类型Props中所有键的联合类型:‘a'|'b'|'c'
2、然后,key in ...就表示key可以是Props中所有键名称中的任意一个
索引查询类型T[P] 语法
用来查询属性的类型,[]中的属性必须存在与被查询类型中,否则会报错
代码示例:
type Props = { a: number; b: string; c: boolean }
type TypeA = Props['a']
TypeA的类型变成了number
索引查询类型也可以同时查询多个索引类型
代码示例:
type Props = { a: number; b: string; c: boolean }
type TypeB = Props['a' | 'b']
type TypeC = Props[keyof Props]
解释:
1、可以使用字符串字面量的联合类型 |
2、也可以使用keyof操作符获取Props中的所有键对应的类型
5、类型声明文件
类型声明文件:用来为已存在的JS库提供类型信息
TS中有两种文件类型:1、.ts文件 2、.d.ts文件
.ts文件
1、既包含类型信息又可以执行代码
2、可以被编译为JS文件,然后,执行代码
3、用途:编写程序代码的地方
.d.ts文件
1、只包含类型信息的类型声明文件
2、不会生成JS文件,仅用于提供类型信息
3、用途:为JS提供类型信息
使用已有的类型声明文件:
1、内置类型声明文件 :TS为JS运行时可用的所有标准化内置API都提供了声明文件
2、第三方库的类型声明文件:库自带类型声明文件或由DefinitelyTyped提供
DefinitelyTyped是一个GitHub仓库,用来提高质量TypeScript类型声明,可以通过npm/yarn来下载TS类型声明包,这些包的名称格式为:@type/*,例如:@type/react等。
TS官方文档:TypeScript: JavaScript With Syntax For Types.
TS中文官方:TypeScript: 中文网 官网 官方.
6、创建自己的类型声明文件
项目内共享类型
使用场景:如果多个.ts文件中都使用到了同一个类型,可以创建.d.ts文件提供该类型,实现类型共享
操作步骤:
1、创建index.d.ts类型声明文件
2、创建需要共享的类型,并使用export导出
3、在需要使用共享类型的.ts文件中,通过import导入即可,.d.ts后缀不用加
import {Props} from './index'
为已有JS文件提供类型声明
使用场景:
1、在将JS项目迁移到TS项目时,为了让已有的.js文件有类型声明
2、成为库作者,创建库给其他人使用
declare关键字:用于类型声明,为其他地方(比如.js文件)已存在的变量声明类型,而不是创建一个新的变量。
代码示例:
declare let count: number
注意:
1、对于type、interface等这些明确就是TS类型的(只在TS中使用的),可以省略declare关键字文章来源:https://uudwc.com/A/6XABj
2、对于let、function等具有双重含义(在JS、TS中都能使用的),应该使用declare关键字文章来源地址https://uudwc.com/A/6XABj