• TypeScript学习笔记


    一、安装ts

    • 命令: 
    npm i -g typescript
    • 查看安装包命令
    tsc -v
    • 显示版本
    Version 5.1.6

    二、ts的常用类型

     1、类型注解

    示例代码

    let age:number = 18

    注意:说明上序代码中的 :number 就是类型注解

    作用:为代码添加类型约束,约定了是什么类型就只能给该类型付同类型的值。否则会报错! 

    2、常用基础类型概括

     1、js已有类型

    • 原始类型:number、string、boolean、null、undefined、symbol
    • 对像类型: object(数组、对象、函数)

    2、ts新增类型

    • 联合类型、 自定义类型、接口、元组、字面量类型、枚举、void、any等

    3、原始类型

    •  原始类型:number、string、boolean、null、undefined、symbol
    • 这些类型完全是按照js原始类型来写的

    4、数组类型

    1、数组类型的两种写法,推荐用第一种

    1. let arrNumber: number[] = [1, 2, 3]
    2. let arrString: Array<string> = ['1', '2', '3']

    5、联合类型 

    注意:一个数组中存在多个类型的数据

    1. //加上小括号说明,数组内的类型可以是小括号里的任何类型
    2. let arr: (number | string | null)[] = [null, 1, '23']
    3. //不加小括号,说明arr即可以是Number类型,也可以是string类型的数组
    4. let arr2: number | string[] = 123
    5. let arr3: number | string[] = ['1', '2', '3']

     解释:|在ts中叫联合类型

     6、类型别名

    • 类型别名:为任意类型取别名
    • 使用场景:当同一类型被多次复杂使用时,可以通过类型别名,简化该类型的使用。
    1. type CustomArray = (number | string)[]
    2. let arr1: CustomArray = ['1', 1]
    3. let arr2: CustomArray = ['1', 1]

    注意:

    1. 使用type关键字来创建类型别名
    2. 类型别名可以是任意合法变量名称        

    7、函数类型

    注意:

    • 函数类型是指:函数参数类型、函数返回值类型 

    1、单独自定参数、返回值的类型

    1. function add(number1:number,number2:number):number{
    2. return number1 + number2
    3. }
    4. console.log(add(1,2));
    1. let add = (number1: number, number2: number): number => {
    2. return number1 + number2
    3. }
    4. console.log(add(1, 3));

    2、同时指定参数和返回值的类型

    注意:这种方法只能用于函数表达式

    1. let add: (number1: number, number2: number) => number = (number1, number2) => {
    2. return number1 + number2
    3. }
    4. console.log(add(1, 3));

    8、 void类型

    注意:如果函数没有返回值,那么函数返回值就是void(就是空值的意思)

    1. function onVoid(num:number):void{
    2. console.log(num);//执行结果:12
    3. }
    4. onVoid(12)

    9、函数可选参数 

    注意:使用函数实现某个功能时,函数参数可传可不传。这种情况在给函数指定参数类型时,就用到可选参数,

     可选参数:在可传可不传的参数后面加

    注意:可选参数后面不能再写必传参数 

    1. function mySlice(start?: number, end?: number) {
    2. console.log('开始', start, '结束', end);
    3. }
    4. mySlice()//开始 undefined 结束 undefined
    5. mySlice(1)//开始 1 结束 undefined
    6. mySlice(1, 2)//开始 1 结束 2

    10、对象类型

    注意:js中的对象是由属性和方法构成的,而TS中对象的类型就是在描述对象的结构(有什么类型的属性和方法)

    解释:

    1. 直接使用 {} 来描述对象结构。属性采用属性名:类型的形式,方法采用方法名:返回值类型的形式
    2. 如果方法有参数,就在方法名后面的小括号内指定参数类型(比如:greet(name:string):void 
    3. 在一行代码中指定对象的多个属性类型时,使用来分割。如果换行可以不用

    对象类型写法:

    1. let obj: { name: string; age: number; onSay(): void } = {
    2. name: '李玉',
    3. age: 21,
    4. onSay() {
    5. console.log('111');
    6. }
    7. }
    8. let obj2: {
    9. name: string
    10. age: number
    11. onSay: (e:number) => void
    12. } = {
    13. name: '简隋英',
    14. age: 28,
    15. onSay(e) {
    16. console.log(e);
    17. }
    18. }

    11、可选属性

    注意:

    1. 对象的属性或方法也是可选的,此时就用到了可选属性了。
    2. 可选属性的语法与可选参数的语法是一致的,都使用来表示 
    1. function myAxios(config: { url: string, methods?: object }) {
    2. }
    3. let config = {
    4. url:'https://www.baidu.com'
    5. }
    6. myAxios(config)

    12、接口

    一、基础用法 

    注意:当一个对象类型被多次使用时,一般会使用接口interface)来描述对象的类型,达到复用的效果。

    解释:

    • 使用interface关键字来声明接口
    • 接口名称可以是任意合法的名称
    • 声明接口后可以直接使用变量的名称作为变量的类型
    • 因为每一行只有一个属性类型,因此属性类型没有{分号} 
    1. interface IPeron {
    2. name: string
    3. age: number
    4. onSay: () => void
    5. }
    6. let obj: IPeron = {
    7. name: '李玉',
    8. age: 21,
    9. onSay() {
    10. }
    11. }

    二、接口和类型别名对比

    interface(接口)和type(类型别名)的对比

    • 相同点:都可以给对象指定类型
    • 不同点

            * 接口interface只能为对像指定类型

            * 类型别名type,不仅可以对对象指定类型,实际上可以为任意类型指定别名

            *type用=(等号),interface不用= (等号)

    interface例子:

    1. interface IPeron {
    2. name: string
    3. age: number
    4. onSay: () => void
    5. }
    6. let obj: IPeron = {
    7. name: '李玉',
    8. age: 21,
    9. onSay() {
    10. }
    11. }

    type例子:

    1. type IPeron = {
    2. name: string
    3. age: number
    4. onSay: () => void
    5. }
    6. let obj: IPeron = {
    7. name: '李玉',
    8. age: 21,
    9. onSay() {
    10. }
    11. }

    type例子: 

    type NumStr = number | string

    三、接口的继承

    注意: 如果两个接口之间有相同的属性或方法,可以将公共属性和方法抽离出来,通过继承来实现复用。

    如下列,这两个接口都有x,y属性,重复写两次太过繁琐。

    1. interface IPearn1 {
    2. x:string,
    3. y:number
    4. }
    5. interface Ipearn2{
    6. x:string,
    7. y:number,
    8. id:number
    9. }

    更好的方式:使用extends关键字的方法去实现继承

    1. interface IPearn1 {
    2. x:string,
    3. y:number
    4. }
    5. interface Ipearn2 extends IPearn1{
    6. id:number
    7. }

    13、元组

     场景:

            在地图中使用经纬度来标记坐标信息。

            可以使用数组来标记坐标,那么,该数组中只有两个元素,并且这两个元素都是数值类型。

    let position: number[] = [12, 23]
    • 使用number[]的缺点:不严谨,因为该类型的数组中可以出现任意多个数字。

    注意:更好的方法是使用元组(Tuple),元组类型是另一种类型的数组,它确切的知道包含多少个元素,以及特定索引对应的类型

    let position: [number, number] = [12, 45]

    解释:

    • 元组类型可以确切的标记出有多少个元素,以及每个元素的类型

    14、类型推论

    注意:在TS中,某些没有声明却指出类型的地方,TS的类型推论机制会帮助提供类型。所以,因为有类型推论的存在,有的地方可以沈略类型注解;

     1、声明变量并且初始化时

    1. let aa = 12
    2. let bb = 'dddd'

    2、决定函数返回值时

    1. function add(num1: number, num2: number) {
    2. return num1 + num2
    3. }
    4. add(1, 2)

    15、类型断言

     注意:有时你会比TS更加明确一个值的类型,此时,可以使用类型断言来指定更具体的类型

    比如:

    注意:getElementById方法返回值的类型是HTMLElement,该类型只包含所有标签的公共属性或方法,不包含a标签特有的href等属性。

    因此,这个类型太宽泛(不具体),无法超做href等a标签特有的属性的方法。

    解决方式:这种情况下就需要使用类型断言指定更加具体的类型。 

    1、类型断言语法1 

    解释:

    • 使用as关键字实现
    • 关键字as后面的类型是一个更加具体的类型(HTMLAnchorElement是HTMLElement的子类型)
    • 通过类型断言,aLink的类型变得更加的具体,这样就可以访问a标签特有的类型或方法了 

     1、类型断言语法2

     注意:另一种写法是,使用<>语法 

    技巧:在浏览器控制台,通过console.dir()打印DOM元素,在属性列表的后面,即可看到该元素的类型。 

    16、字面量类型

    思考以下代码,两个变量的类型分别是?

    通过TS类型推论机制,可以得到答案:

    1. 变量str1的类型为:string
    2. 变量str2的类型为:'Hello TS'

    解释:

    1. str1是一个变量(let),它的值可以是任意字符串,所以类型是string
    2. str2是一个常量(const),它的值不能变化,所以类型是'Hello TS'

    注意:此处的 'Hello TS'就是字面量类型。也就是说某个特定字符串也可以作为TS中的类型。

    除字符串外,任何的JS字面量(比如、对象、数字等)都可以作为类型使用。

    1. let str1 = 'Hello'
    2. const str2: 'Hello' = 'Hello'
    3. let age: 18 = 18

    使用模式:字面量类型配合联合类型(|)一起用

    使用场景:用来表示一组明确的可选值列表。 

    比如贪吃蛇游戏中,游戏的方向就只有(上下左右)任意一个

     解释:参数direction的值只能是up/down/left/right中的任意一个。

    优势:相比于string类型,使用字面量类型更加精确、严谨。

     17、枚举

    1.1、基本枚举

    注意:形参direction的类型为枚举Direction,那么,实参的值就应该是枚举Direction成员中的任意一个

    1. enum Direction { UP, Down, Left, Right }
    2. function changeDirection(direction: Direction) {
    3. console.log(direction);
    4. }
    5. changeDirection(Direction.UP)
    6. changeDirection(Direction.Down)
    7. changeDirection(Direction.Left)
    8. changeDirection(Direction.Right)

    解释:类似于JS中的对象,直接通过点(.)语法访问枚举成员。 

    1.2、 枚举成员的值以及数字枚举

    问题: 我们将枚举成员作为函数的实参,它的值是什么?

    解释:通过将鼠标移入Direction.UP可以看到枚举成员UP的值是0

    注意:枚举成员是有值的,默认为:从0开始自增的数字 

    我们把,枚举成员的值为数字枚举

    当然也可以给枚举中的成员初始化值:

    1. //UP=10, Down=11, Left=12, Right=13
    2. enum Direction { UP = 10, Down, Left, Right }
    3. //UP=10, Down='abc', Left=8, Right=4
    4. enum Direction { UP = 10, Down = 5, Left = 8, Right = 4 }

    1.3、字符串枚举

     注意:枚举成员的值是字符串

    1. //UP=UP, Down='Down', Left='Left', Right='Right'
    2. enum Direction { UP = 'UP', Down = 'Down', Left = 'Left', Right = 'Right' }
    3. function changeDirection(direction: Direction) {
    4. console.log(direction);
    5. }
    6. changeDirection(Direction.UP)
    7. changeDirection(Direction.Down)
    8. changeDirection(Direction.Left)
    9. changeDirection(Direction.Right)

    注意:字符串枚举没有自增长行为,因此,字符串枚举成员都必须拥有初始值

     1.4、枚举的特点及其原理

    枚举是TS为数不多的非JS类型级拓展(不仅仅是类型)的特征之一

    因为:其他类型不仅仅被当做类型,而是枚举不仅用于类型还提供值(枚举成员都是有值的)

    也就是说,其他类型会在编译成JS代码时直接移除,但是,枚举类型会被编译成JS代码

     TS:

    1. //UP=UP, Down='Down', Left='Left', Right='Right'
    2. enum Direction { UP = 'UP', Down = 'Down', Left = 'Left', Right = 'Right' }
    3. function changeDirection(direction: Direction) {
    4. console.log(direction);
    5. }
    6. changeDirection(Direction.UP)
    7. changeDirection(Direction.Down)
    8. changeDirection(Direction.Left)
    9. changeDirection(Direction.Right)

    JS: 

    1. //UP=UP, Down='Down', Left='Left', Right='Right'
    2. var Direction;
    3. (function (Direction) {
    4. Direction["UP"] = "UP";
    5. Direction["Down"] = "Down";
    6. Direction["Left"] = "Left";
    7. Direction["Right"] = "Right";
    8. // Direction为对象,UP,Down。。。为属性加赋值
    9. })(Direction || (Direction = {}));
    10. //传入Direction=understand||Direction = {}
    11. function changeDirection(direction) {
    12. console.log(direction);
    13. }
    14. changeDirection(Direction.UP);
    15. changeDirection(Direction.Down);
    16. changeDirection(Direction.Left);
    17. changeDirection(Direction.Right);

    说明:枚举类型和前面说到的字面量类型+联合类型组合功能类似,都用来表示一组明确的可选值列表。

    一般情况下,推荐使用字面量类型+联合类型,因为相比于枚举这种方式更加简洁、高效。

    18、any类型 

    原则:不推荐使用any!这会让TS变成"AnyScript"(失去类型保护的优势)

    因为当值的类型为any时,可以对该值进行任意超做,并且不会有任何提示。

    其他隐式具有any类型的情况:

    • 申明变量不提供类型也不提供默认值
    • 函数参数不加类型

    19、TS中的typeof

     众所周知,JS提供了typeof运算符,用来在js中获取数据的类型

    console.log(typeof 'hello'); //打印 string

    实际上TS也提供了typeof操作符:可以在类型上下文中引用变量或属性的类型(类型查询)。

    使用场景:根据已有变量的值,来获取该值的类型,来简化类型书写。

    1. let parameter = {
    2. x: 10,
    3. y: 20
    4. }
    5. function forMater(point: { x: number, y: number }) {}
    6. forMater(parameter)
    1. let parameter = {
    2. x: 10,
    3. y: 20
    4. }
    5. let p = {
    6. x: 0,
    7. y: 0
    8. }
    9. function forMater(point: typeof p) {}
    10. forMater(parameter)

    三、TypeScript高级类型

     class

     1、calss的基本使用

    TS全面支持ES5中引入的class关键字

    • 添加属性的方法有以下两种:
    1. class Person{
    2. //添加属性的方法有以下两种
    3. name1:string
    4. name2 = '简隋英'
    5. }
    6. const p = new Person()
    7. p.name1//属性是string
    8. p.name2//属性是string

    解释:

    1. 根据TS中的类型推论,可以知道Person类的实例对象p的类型是Person
    2. TS中的class,不仅仅提供class的语法功能,也作为一种类型存在。 

     2、calss的构造函数

    1. class Person{
    2. //添加属性的方法有以下两种
    3. name1:string
    4. name2 = '简隋英'
    5. constructor(str1:string,str2:string){
    6. this.name1 = str1
    7. this.name2 = str2
    8. }
    9. }
    10. const p = new Person('李玉','简隋英')
    11. p.name1//属性是string
    12. p.name2//属性是string

    解释:

    1. 成员初始化(比如:name1:string )后,才能通过this.name1来访问实例成员 
    2. 需要为构造函数指定类型注解,否则会被隐式推断为any,构造函数不需要返回值

    3、class实例方法 

    1. class Person {
    2. x = '简隋英'
    3. y = '李玉'
    4. scale(name1: string, name2: string): void {
    5. this.x = name1
    6. this.y = name2
    7. }
    8. }
    9. const p = new Person()
    10. p.scale('江停', '严峫')
    11. console.log(p.x, p.y);

     解释:方法的类型注解(参数和返回值)于函数相同。

    4、class的继承 

    注意:类的继承有两种方式extends(继承父类)implements(实现接口)

     extends(继承父类) 
    1. class Name{
    2. onBackName(){
    3. console.log('江停');
    4. }
    5. }
    6. class Person extends Name{
    7. }
    8. const p = new Person()
    9. p.onBackName() //结果打印:江停

     解释:

    1. 通过extends关键字实现继承
     implements(实现接口)
    1. interface Singable{
    2. name:string
    3. sing():void
    4. }
    5. class Person implements Singable{
    6. name: string
    7. sing(): void {
    8. console.log('夫妻双双把家还!');
    9. }
    10. }

    解释:

    1. 通过关键字implements让class实现继承
    2. implements实现继承,要继承接口所有的属性和方法,少一个都会报错! 

    5、class类的可见性

    类成员的可见性,可以使用TS来控制class的方法或属性对于class外的代码是否可见。

    可见性修饰符包括:1、public(公有的)2、protected(受保护的)3、privated(私有的)

    public(公有的)
    1. class Person {
    2. name = '江停'
    3. public sing(name: string): void {
    4. console.log(this.name + name);
    5. }
    6. }
    7. const p = new Person()
    8. p.sing('严峫')//结果打印:江停严峫

    注意:

    1. 在类属性或方法前面加public关键字,来修饰该属性或方法是共有的。
    2. 因为public是默认可见性,所以,可以直接省略。 

    protected(受保护的)
    1. class Name {
    2. protected member() {
    3. console.log('吴雩');
    4. }
    5. cole() {
    6. this.member//这里可以调用
    7. }
    8. }
    9. const nameClass = new Name()
    10. nameClass.cole()//这个方法可以调用
    11. nameClass.member()//这个方法不可以调用
    12. class Person extends Name {
    13. name = '江停'
    14. sing(name: string): void {
    15. this.member()
    16. }
    17. }
    18. const p = new Person()
    19. p.sing('严峫')//结果打印:江停严峫
    20. p.cole()//这个方法可以调用
    21. p.member()//这个方法不可以调用

     注意:

    1. 在类属性或方法前面添加protected关键字,来修饰该属性或方法是受保护的。
    2. 在子类的方法内部可以通过this来访问父类中受保护的成员,但是,对实例不可见

    privated(私有的)
    1. class Name {
    2. private member() {
    3. console.log('吴雩');
    4. }
    5. cole() {
    6. this.member//这里可以调用
    7. }
    8. }
    9. const nameClass = new Name()
    10. nameClass.cole()//这个方法可以调用
    11. nameClass.member()//这个方法不可以调用
    12. class Person extends Name {
    13. name = '江停'
    14. sing(name: string): void {
    15. this.member()//这个方法不可以调用
    16. }
    17. }
    18. const p = new Person()
    19. p.sing('严峫')//结果打印:江停严峫
    20. p.cole()//这个方法可以调用
    21. p.member()//这个方法不可以调用

    注意:

    1. 在类属性或方法前面添加private关键字,来修饰该属性或方法是私有的。
    2. 私有属性或方法只有在当前类中可见,对子类和实例对象也都是不可见的!

    6、readonly只读修饰符 

    出来可见修饰符以外,还有一个常见修饰符就是:readonly(只读修饰符)

    readonly:表示只读,用来防止构造函数以外的属性对他进行赋值

    class中

    注意:只能在constructor中修改属性,其他地方不能。且readonly只能用于属性不能用于方法。

    1. class Person{
    2. readonly age:number = 18
    3. constructor(age1:number){
    4. this.age = age1 //只能在这里改
    5. }
    6. changAge(){
    7. this.age = 23 //修改属性会报错
    8. }
    9. }
    10. const p = new Person(21)

    class中 

    注意:如果 readonly的属性没有增加类型注解,那么值的属性就是他的初始值,相当于一个常量。就算是在constructor里也不能改。

    1. class Person{
    2. readonly age = 18
    3. constructor(age1:number){
    4. this.age = age1 //这里也不能改
    5. }
    6. changAge(){
    7. this.age = 23 //修改属性会报错
    8. }
    9. }
    10. const p = new Person(21)

    interface中 
    1. interface IPerson{
    2. readonly name:string
    3. }
    4. let obj:IPerson ={
    5. name:'严峫'
    6. }
    7. obj.name = '江停'//name属性标记只读,所以不能被复制

    {}中
    1. let obj: { readonly name: string } = {
    2. name: '严峫'
    3. }
    4. obj.name = '江停'//name属性标记只读,所以不能被复制

     解释: 

    1. 使用readonly关键字修饰该属性是只读的,注意只能修饰属性不能修饰方法
    2. 注意:在class属性中如果不加类型注解,则属性的类型是初始化时的常量。
    3. 接口或{}表示的对象类型,也可以使用readonly

    类型兼容性

    1、基本理解 

    两种类型系统:结构化类型系统和标明类类型系统

    结构化类型系统 

    TS采用的是结构化类型系统,也叫做duck typing(鸭子类型),类型检查关注的是值所具备的性状

    也就是说,结构化类型系统中,如果两个对象具有相同的形状,则认为他们属于同一类型。

    1. class Name {
    2. x:number
    3. y:number
    4. }
    5. class Name2{
    6. x:100
    7. y:200
    8. }
    9. const p:Name = new Name2()

    解释:

    1. Name和Name1是两个名称不相同的类
    2. 变量p的类型被显示标注为Name类型,但是她的值却是Name1的实例,并没有类型错误
    3. 因为TS是结构化类型系统,只检查Name和Name1的结构是否相同
    4. 但是如果在标明类类型系统中,他们是不同的类,类型无法兼容

     2、对象类型兼容性

    注意:结构化类型系统中,如果两个对象具有相同的形状,则认为他们属于同一类型,这种说法不完全准确。

    更准确的说法是:对于对象类型来说,y的成员至少于x相同,则认为x兼容y(成员多的可以赋值给少的)

    1. class Point {
    2. x: number
    3. y: number
    4. }
    5. class Point3D {
    6. x: number
    7. y: number
    8. z: number
    9. }
    10. const p: Point = new Point3D

    解释:

    1. Point3D的成员至少要与Point相同,则Point兼容Point3D
    2. 所以,成员多的Point3D可以赋值给成员少的Point

    3、接口之间的兼容性

    除了class之外,TS中的其他类型也存在相互兼容的情况,包括:1、接口兼容 。2、函数兼容性等。

    接口之间的兼容性,类似于class,并且,class和interface之间也可以兼容

    4、函数之间的类型兼容性 

    函数之间的兼容性比较复杂,需要考虑:1、参数个数 2、参数类型 3、返回值类型

    参数个数

    参数个数,参数多的兼容参数少的(或者说,参数少的可以赋值给多的

    1. type F1 = (a:number) =>void
    2. type F2 = (a:number, b:number)=>void
    3. let a:number = 12
    4. let f1:F1 = (a) => {
    5. return null
    6. }
    7. let f2:F2 = f1
    1. const arr = ['a', 'b', 'c']
    2. arr.forEach(() => { })
    3. arr.forEach((item) => { })

    解释:

    1. 参数少的可以赋值给参数多的,所以,f1可以赋值给f2
    2. 数组foeEach方法的第一个参数是回调函数,该实例中类型为(value:string,index:number,array:string[])=> void
    3. 在JS中沈略用不到的函数参数是正常的实际上是很常见的,这样的使用方式,促成了TS中函数类型之间的兼容性。
    4. 并且因为回调函数是有类型的,所以,TS会自动推导出参数item,index,array的类型

    类型属性

    参数类型:相同位置的参数类型要相同,(原始类型)或兼容(对象类型)

    •  原始类型
    1. type F1 = (a: number) => void
    2. type F2 = (a: number) => void
    3. let f1:F1
    4. let f2:F2
    5. let num = 12
    6. f2 = (num) => {}
    7. f1 = f2
    8. f2 = f1
    • 对象类型
    1. interface Point2D {
    2. x: number
    3. y: number
    4. }
    5. interface Point3D {
    6. x: number
    7. y: number
    8. z: number
    9. }
    10. type F2 = (p: Point2D) => void
    11. type F3 = (p: Point3D) => void
    12. let p1 = { x: 12, y: 10 }
    13. let f2: F2 = (p1) => { }
    14. let f3: F3 = f2
    15. f2 = f3//会报错,多的不能赋值给少的

    解释:

    1. 注意,此处于前面讲的接口兼容性冲突
    2. 技巧,将对象拆开,把每个属性看做一个个参数,则,参数少的f2可以赋值给参数多的f1

    返回值
    •  例子1:
    1. type F5 = () => string
    2. type F6 = () => string
    3. let f5:F5 = () => {return '111'}
    4. let f6:F6 = f5
    • 例子2:
    1. type F7 = () => { name: string }
    2. type F8 = () => { name: string, age: number }
    3. let f7: F7 = () => {
    4. return {
    5. name:'李玉'
    6. }
    7. }
    8. let f8: F8 = () => {
    9. return {
    10. name: '简隋英',
    11. age: 28
    12. }
    13. }
    14. f7 = f8
    15. console.log(f7);//打印结果:[Function: f8]

    解释:

    1. 如果返回值类型是原始类型,此时两个类型要相同,比如例子1的类型F5和F6
    2. 如果返回值类型是对象类型,此时成员多的可以赋值给成员少的,比如例子2 的类型F7和F8

     交叉类型

     1、基本介绍

    交叉类型&):功能类型接口继承(extends),用于组合多个类型为一个类型常用于对象类型) 

    •  extends:
    1. interface Person{
    2. name:string
    3. }
    4. interface Content extends Person{
    5. age:number
    6. }
    7. let PersonContent:Content = {
    8. name:'简隋英',
    9. age:27
    10. }
    •  交叉类型(&):
    1. interface Person{
    2. name:string
    3. }
    4. interface Content{
    5. age:number
    6. }
    7. type PersonContent = Person & Content
    8. let obj:PersonContent = {
    9. name:'简隋英',
    10. age:27
    11. }

     解释:使用交叉类型后,新的类型PersonContent就同时具备了Person 和 Content的所有属性类型。

    • 相当于:
    type PersonContent = { name: string, age: number }

    2、交叉型和接口型的对比说明 

     交叉型(&)和接口型(extends)的对比:

    • 相同点:都可以实现对象类型的组合
    • 不同点:两种方法实现实现类型组合时,对于同名属性之间,处理类型冲突的方式不同
    1. 接口型(extends):
    1. interface A {
    2. fn: (value: number) => string
    3. }
    4. interface B extends A{//B不兼容
    5. fn: (value: string) => string
    6. }
    1. 交叉型(&) :
    1. interface A {
    2. fn: (value: number) => string
    3. }
    4. interface B {//B不兼容
    5. fn: (value: string) => string
    6. }
    7. type C = A & B

    说明:以上代码,接口继承会报错(类型不兼容);交叉类型没有错误,可以简单理解为:

    fn: (value: string | number) => string

    泛型和keyof

    1、泛型的基本使用 

    泛型是在保证类型安全的前提下,让函数等与多种类型一起工作,从而实现复用,常用于:函数、接口、class中。

    需求 :创建一个id函数,传入什么数据就返回该数据本身。(也就是说参数和返回值类型相同)

    1. function id(value:number):number {
    2. return value
    3. }

     比如:id(10)调用以上函数就会直接返回10本身。但是,该函数只能用于数值类型,无法用于其他类型。

    为了能让函数接收任意类型,可以将参数类型修改为any,但是,这样就会失去TS的类型保护,类型不安全。

    1. function id(value:any):any {
    2. return value
    3. }

    解释:泛型在保证类型安全(不丢失类型信息)的同时,可以让函数等与多种不同的类型一起工作,灵活复用。

    创建泛型函数 
    1. function id<Type>(value: Type): Type {
    2. return value
    3. }

    解释:

    1. 语法:在函数名称后面添加一个<>(尖括号),尖括号中添加类型变量,比如此处的Type
    2. 类型变量Type,是一种特殊类型的变量,它处理类型而不是值。
    3. 该类型变量相当于一个类型容器,能够捕获用户提供的类型(具体是什么类型由用户调用该函数时决定)
    4. 因为Type是类型,因此可以将其作为函数参数和返回值的类型,表示参数和返回值具有相同的类型。
    5. 类型变量Type,可以是任意合法的类型
    调用泛型类型
    1. function id<Type>(value: Type): Type {
    2. return value
    3. }
    4. const num1 = id<number>(10)
    5. const str = id<string>('江停')
    6. const beal = id<boolean>(true)

    解释:

    1. 语法:在调用函数的后面加<>(尖括号),尖括号中指定具体的类型,比如,此处的number
    2. 当传入类型number后这个类型就会被函数声明时指定的类型变量Type捕获。
    3. 此时,Type的类型就是number,所以函数id参数和返回值的类型都是number。

    同样,如果传入类型string,函数id参数和返回值的类型就是string

    这样,通过泛型就做到了让id函数于多种不同的类型一起工作,实现了复用的同时保证了类型安全

     2、简化泛型函数调用

    1. function id<Type>(value: Type): Type {
    2. return value
    3. }
    4. const num1 = id(10)
    5. const str = id('江停')
    6. const beal = id(true)

    解释:

    1. 在调用泛型函数时,可以省略<类型>来简化泛型函数的调用
    2. 此时,TS内部会采用一种叫做类型参数推断的机制 ,来根据传入的实参自动推断出类型变量Type的类型。
    3. 不如,传入实参10,Ts会自动推断出变量num的类型为number,并且作为Type的类型

    推荐:使用这种简化的方式调用泛型函数,使代码变得更短,更加易读

    说明:当编译器无法推断类型或者类型不准确时,就需要显式的传入类型参数

    3、泛型约束

    泛型约束:默认情况下,泛型函数的类型变量Type可以代表多个类型,这导致无法访问任何属性。

    比如:id('a')调用函数时获取参数长度:

    1. function id<Type>(value: Type): Type {
    2. console.log(value.length);//这个代码报错
    3. return value
    4. }
    5. const num1 = id([10,12])

    解释:Type可以代表任意类型,无法保证一定存在length属性,比如number类型就没有length。

    此时,就需要为泛型添加约束收缩类型(缩小类型的取值范围)

    添加泛型的收缩属性,主要有以下两种方式:指定更加具体的类型 和 添加约束

    指定更加具体的类型 
    1. function id<Type>(value: Type[]): Type[] {
    2. console.log(value.length);//这个代码报错
    3. return value
    4. }
    5. const num1 = id([10,12])

     比如:将类型修改为Type[](Type类型的数组)。因为只要是数组就一定存在length属性,因此就可以访问了

    添加约束extends
    1. interface ILength{
    2. length:number
    3. }
    4. function id<Type extends ILength>(value: Type): Type {
    5. console.log(value.length);//这个代码报错
    6. return value
    7. }
    8. const num1 = id([10,12])
    9. const str = id('江停')
    10. const beal = id({length:12,name:'江停'})

     解释:

    1. 创建描述约束的接口ILength,该接口要提供ILength属性
    2. 通过extends关键字使用该接口,为泛型(类型变量)添加约束
    3. 该约束表示:传入的类型必须具有length属性

     注意:传入的实参(比如,数组)只要有length属性即可,这也符合前面讲到的接口的类型兼容性

    多个泛型变量的情况 

     泛型的类型变量可以有多个,并且类型变量之间还可以约束(比如:第二个类型变量受第一个类型变量约束)。

    比如,创建一个函数来获取对象中属性的值:

    1. function getProp<Type,Key extends keyof Type>(obj:Type,key:Key){
    2. return obj[key]
    3. }
    4. let person = {name:'江停',age:31}
    5. console.log(getProp(person,'name'));

    解释:

    • 添加了第二个类型变量Key,两个类型变量之间使用(,)逗号分隔。
    • keyof关键字接收一个对象类型,生成其键名称(可能是字符串或数字)的联合类型
    • 本实例中keyof Type实际上获取的是person对象所有联合类型,也就是:‘name’|‘age’
    • 类型变量Key受Type约束,可以理解为:key只能是Type所有键中的任意一个,或者说只能访问对象中纯在的属性

    索引签名类型和索引查询类型 

    映射类型 

  • 相关阅读:
    python对于grpc的简单操作(二)
    【MySQL】表复制,去重,合并查询
    SpringBoot 使用 Feign 无废话 All-in-one 指南
    【C++入门】C和C++混合编程超详细讲解
    婚纱摄影行业,还在使用电梯广告?不如试试大数据精准营销
    [通用]计算机经典面试题基础篇Day3
    Vue的MVVM实现原理
    ThreeJS-3D教学五-材质
    【Java基础】 多线程
    vuex的使用
  • 原文地址:https://blog.csdn.net/Seven_Ting/article/details/133692142