• TypeScript快速上手


    文章目录


    前言

    哔哩哔哩课程链接: https://www.bilibili.com/video/BV14Z4y1u7pi?p=89&vd_source=92ee1c593b77f3dfb92819ea75999078


    一、TypeScript是什么

    TypeScript是JavaScript的超级 在JS的基础上增加了类型支持


    二、TypeScript的好处

    添加类型支持的好处:提高开发效率 减少找Bug的时间


    三、TypeScript的优势

    TS的优势:之一 类型推断机制 不需要在每个地方都显示标注类型


    四、TypeScript的开发环境

    安装环境 浏览器/node 只认识js代码 不认识ts代码 需要先将ts——>js 才能运行


    五、编译并运行TS代码

    1创建TS 2编译TS 3执行TS
    执行指令 node .\test.js
    注意:由TS编译生成的JS文件 代码中没有类型信息
    ts:
    let a : number = 100
    console.log(a)
    js:
    var a = 100;
    console.log(a);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    六、 简化运行TS的步骤

    问题:每次修改代码后 都要重复执行两个命令 才能运行TS 繁琐
    简化方式:使用ts-node包 直接在node.js中执行ts代码
    安装命令:npm i -g ts-node
    使用方式:ts-node hello.ts
    分析:ts-node命令在内部已经将TS->JS 再运行js代码


    七、TS常用的类型

    number、string、boolean、null、undefined、symbol


    八、类型注解

    为变量约定了类型 传值不符合规范会报错


    九、常用的类型

    JS已有的类型:
    原始类型:number/string/boolean/null/undefined/symbol
    对象类型:object (数组 对象 函数)
    TS新增类型
    联合类型 自定义类型(类型别名) 接口 元组 字面量类型 枚举 void any等等
    
    • 1
    • 2
    • 3
    • 4
    • 5

    十、数组类型


    十一、联合类型

    需求 数组中既有number类型 又有string类型


    十二、pandas是什么?

    类型别名(自定义类型):为任意类型起别名
    使用场景:当同一类型(复杂)被多次使用时 可以通过类型别名 简化该类型的使用

    type CustomArray = (number | string)[ ]
    let arr1: CustomArray = [1,'a',3,'b']
    let arr2: CustomArray = ['x','y',6,7]
    
    • 1
    • 2
    • 3

    解释:
    《1》使用type关键字来创建类型别名
    《2》类型别名 比如 此处的CustomArray 可以是任意合法的变量名称
    《3》创建类型别名后 直接使用该类型别名作为变量的类型注解即可


    十三、函数类型 实质就是给函数参数和返回值加类型

    两种方式:

    《1》单独指定参数 返回值的类型

    function add(num1:number,num2:number):number{
    return num1+num2
    }
    
    const add = (num1:number,num2:number):number=>{
    return num1+num2
    }
    
    const add = () =>{} //箭头函数
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    《2》同时指定参数 返回值的类型

    const cat: (num1: number, num2: number) => number = (num1, num2) => {
        return num1 - num2
    }
    
    console.log(cat(2, 1))
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    十四、void类型

    函数没有返回值 返回值类型为void


    十五、函数类型

    使用函数实现某个功能时 函数可以传参数也可以不传参数 在这种情况下
    在给函数参数指定类型时 就可以用到可选参数了

    function mySlice(start?:number,end?:number):void{
    console.log('起始索引:',start,'结束索引:',end)
    }
    
    • 1
    • 2
    • 3

    可选参数:在可传可不传的参数名称后面添加?
    注意:可选参数只能出现在参数列表的最后 就是说可选参数后面不能出现必选参数


    十六、对象类型(有什么类型的属性和方法)

    写法:

    let person: { name: string, age: number, sayHi(): void } = {
        name: "张三",
        age: 10,
        sayHi: () => { }
    }
    
    console.log(person)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    注意: <1>如果一行代码只指定一个属性类型(通过换行来分割多个属性类型)可以去掉分号 <2>方法的类型也可以使用箭头函数的形式 sayHi:()=>void


    十七、对象类型

    可选属性的语法与函数可选参数的语法是一致的 都使用?来表示

    function myAxios(config: { url: string, method?: string }) {
        console.log(config.url)
    }
    myAxios({
        url: 'https:www.baidu.com'
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    十八、接口

    接口 当一个对象类型被多次使用时 一般会使用接口来描述对象的类型 达到复用的目的

    interface PersonI {
        name: string
        age: number
        sayHi: () => void
    }
    
    let personi:PersonI ={
        name: "personi",
        age: 20,
        sayHi: function (): void {
            throw new Error("Function not implemented.")
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    解释:
    《1》使用interface关键字来声明接口
    《2》接口名称可以是任意合法的变量名称
    《3》声明接口后 直接使用接口名称作为变量的类型
    《4》因为每一行只有一个属性类型 因此 属性类型后没有;号


    十九、interface 和type的对比

    相同点: 都可以给对象指定类型
    不同点:类型别名 不仅可以为对象指定类型 实质上可以为任意类型指定别名


    二十、接口继承

    interface Point2D {
        x: number
        y: number
    }
    
    interface Point3D extends Point2D {
        z: number
    } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    二十一、元组

    场景:在地图中 使用经纬度坐标来标记位置信息。
    可以使用数组来记录坐标 那么 该数组中只有两个元素 并且这两个元素都是数值类型

    let position: number[] = [39.5427,116.2317]
    
    • 1

    使用number的缺点:不严谨 因为该类型的数组中可以出现任意多个数字
    更好的方式:元组(Tuple)
    元组类型是另一种类型的数组,它确切地知道包含多少个元素 以及特定索引对应的类型

    let position : [number,number]=[39.5427,116.2317]
    
    • 1

    解释:
    《1》元组类型可以确切地标记出有多少个元素 以及每个元素的类型
    《2》该示例 元素中有两个元素 每个元素的类型都是number


    二十二、类型推论

    在TS中 某些没有明确指出类型的地方 TS的类型推论机制会帮助提供类型

    let age = 13
    
    function addL(num1: number, num2: number) {
        return num1 + num2
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    二十三、类型断言

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

    console.log($0)
    console.dir($0)
    
    • 1
    • 2

    解释:
    《1》使用as关键字 实现类型断言
    《2》关键字as后面的类型是一个更加具体的类型
    《3》通过类型断言 aLink的类型变得更加具体 这样访问a标签特有的属性和方法
    // const aLink = document.getElementById(‘link’) as HTMLAnchorElement
    const aLink = document.getElementById(‘link’)
    aLink.href


    二十四、字面量类型

    let str1 = "Hello TS"
    // let str1: string
    const str2 = "Hello TS"
    // const str2: "Hello TS"
    
    
    • 1
    • 2
    • 3
    • 4
    • 5

    使用模式:字面量类型配合联合类型一起使用
    使用场景:用来表示一组明确的可选值列表

    比如 在贪吃蛇游戏中 游戏的方向的可选值只能是上,下,左,右

    function changeDirection(direction: 'up' | 'down' | 'left' | 'right') {
        console.log(direction)
    }
    
    changeDirection('up')
    
    • 1
    • 2
    • 3
    • 4
    • 5

    解释:参数direction的值只能是up/down/left/right
    优势:相比于string类型 使用字面量类型更加精确 严谨


    二十五、枚举

    枚举的功能类似于字面量类型+联合类型的组合功能 也可以用于表示一组明确的可选值
    枚举:定义一组命名常量 它描述一个值 该值可以是这些命名常量中的一个

    enum Direction { Up, Down, Left, Right }
    
    function changeDirection1(direction: Direction) {
        console.log(direction)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    解释:
    《1》使用enum 关键字定义枚举
    《2》约定枚举名称 枚举中的值以大写字母开头
    《3》枚举中的多个值之间通过(逗号)分割
    《4》定义好枚举之后 直接使用枚举名称作为类型注解

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

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


    二十六、字符串枚举

    字符串枚举:枚举成员的值是字符串
    字符串枚举没有自增长的行为 因此 字符串枚举的每个成员必须有初始值

    enum Direction {
        Up = 'UP',
        Down = 'DOWN',
        Left = 'LEFT',
        Right = 'RIGHT'
    }
    
    function changeDirection1(direction: Direction) {
        console.log(direction)
    }
    
    changeDirection1(Direction.Up)
    changeDirection1(Direction.Down)
    changeDirection1(Direction.Left)
    changeDirection1(Direction.Right)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    二十七、枚举的特点及原理

    枚举是TS为数不多的非JavaScript类型级扩展(不仅仅是类型)的特性之一。
    因为:其他类型仅仅被当作类型 而,而枚举不仅用作类型 还提供值(枚举成员都是有值的)。
    也就是说 其他的类型会在编译为JS代码时自动移除 但是 枚举类型会被编译为JS代码

    enum Direction {
        Up = 'UP',
        Down = 'DOWN',
        Left = 'LEFT',
        Right = 'RIGHT'
    }
    
    var Direction;
    (function (Direction) {
        Direction["Up"] = "UP";
        Direction["Down"] = "DOWN";
        Direction["Left"] = "LEFT";
        Direction["Right"] = "RIGHT";
    })(Direction || (Direction = {}));
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    二十八、any类型

    不推荐使用any 这会让TypeScript变为 AnyScript 失去TS类型保护的优势

    因为当值的类型为any时 可以对该值进行任意操作 并且不会有代码提示

    解释:
    《1》以上操作都不会有任何类型错误提示,即使可能存在错误
    《2》其它 隐式具有any类型的情况
    1.声明变量不提供类型也不提供默认值
    2.函数参数不加类型


    二十九、typeof

    用来在JS中获取数据的类型

    console.log(type "Hello World") //打印string
    
    
    • 1
    • 2

    实际上 TS也提供了typeof操作符 可以在类型上下文中引用变量或属性的类型(类型查询)
    使用场景:根据已有变量的值 获取该值的类型 来简化类型书写

    let p = {x:1,y:2}
    function formatPoint(point:{x:number;y:number}) {}
    formatPoint(p)
    
    function formatPoint(point:typeof p) {}
    
    
    let p = { x: 1, y: 2 }
    
    // function formatPoint(point: { x: number; y: number }) {console.log("hello") }
    function formatPoint(point: typeof p) { console.log("hello") }
    formatPoint(p)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    解释:
    《1》使用typeof操作符来获取变量p的类型 结果与第一种(对象字面量形式的类型)相同
    《2》typeof 出现在类型注解的位置(参数名称的冒号后面)所处的环境就在类型上下文(区别于JS代码)
    《3》注意:typeof只能用来查询变量或属性的类型 无法查询其他形式的类型(比如 函数调用的类型)


    三十、TypeScript高级类型概述

    1.class类

    TypeScript全面支持ES2015中引入的class关键字 并为其添加了类型注解和其他语法(比如 可见性修饰符等)
    class基本使用 如下:

    class Person {}
    const per = new Person()
    
    • 1
    • 2

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

    实例属性初始化

    class Person{
    age: number
    gender = '男'
    // gender : string = '男'
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    解释:
    1》声明成员age 类型为number(没有初始值)
    2》声明成员gender 并设置初始值 此时 可省略类型注解(TS类型推论为string类型)

    2.类型兼容性

    3.class类的构造函数

    class Person{
    age : number
    gender : string
    constructor(age:number,gender:string){
    this.age = age
    this.gender = gender
    }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

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

    4.class实例方法

    class Point {
        x = 10
        y = 10
    
        scale(n: number): void {
            this.x *= n
            this.y *= n
        }
    }
    
    const point = new Point()
    point.scale(10)
    console.log(point.x, point.y)
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

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

    5.class继承

    类继承的两种方式
    《1》extends(继承父类)
    《2》implements(实现接口)
    说明:JS中只有extends 而implements是TS提供的

    class Animal {
        move() { console.log('Moving along!') }
    }
    class Dog extends Animal {
        bark() { console.log('汪!') }
    }
    
    const dog = new Dog
    dog.move()
    dog.bark()
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    解释:
    《1》通过extends关键字实现继承
    《2》子类Dog继承父类Animal 则Dog的实例对象dog就同时具有了父类Animal和子类Dog的所有属性和方法

    6.class继承 接口

    interface Singable {
        sing(): void
    }
    
    class Person implements Singable {
        sing(): void {
            console.log('我是你的小呀小苹果')
        }
    }
    
    const per = new Person()
    per.sing()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    解释:
    《1》通过implements关键字让class实现接口
    《2》Person类实现接口Singable意味着 Person类中必须提供Singable接口中指定的所有方法和属性。

    7.类成员可见性

    类成员可见性:可以使用TS来控制class的方法或属性对于class外的代码是否是可见的
    可见性修饰符包括:
    《1》public 公有的 公开的
    《2》protected 受保护的
    《3》private 私有的

    class Animal {
       public move() {
            console.log('Moving along!')
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

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

    protected 表示受到保护的 仅对其声明所在的类和子类中(非实例对象)可见。

    class Animal{
    protected move(){console.log('Moving along!')}
    }
    class Dog extends Animal{
    bark(){
    console.log('汪!')
    this.move()
    }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

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

    class类的可见性修饰符
    private 表示私有的 只在当前类中可见 对实例对象以及子类也是不可见的

    class Animal {
    private move(){ console.log('Moving along!')}
    walk(){
       this.move()
    }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

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

    readonly 只读修饰符

    除了可见性修饰符之外 还有一个常见的修饰符就是:readonly(只读修饰符)
    readonly:表示只读 用来防止在构造函数之外对属性进行赋值

    class Person {
        readonly age: number = 18
        constructor(age: number) {
            this.age = age
        }
    }
    
    const z = new Person(11)
    z.age =1
    // 无法分配到 "age" ,因为它是只读属性。ts(2540)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    解释:
    《1》使用readonly关键字修饰该属性是只读的 注意只能修饰属性不能修饰方法
    《2》注意:属性age后面的类型注解(比如 此处的number)如果不加 则age的类型为18(字面量类型)
    《3》接口或者{}表示的对象类型 也可以使用readonly


    三十一、类型兼容性

    两种类型系统:
    《1》Structural Type System(结构化类型系统)
    《2》 Nominal Type Syatem (标明类型系统)
    TS 采用的是结构化类型系统 也叫做duck typing(鸭子类型)类型检查关注的是值所具有的形状

    class Point {x:number;y:number}
    class Point2D {x:number;y:number}
    
    const p : Point = new Point2D()
    
    • 1
    • 2
    • 3
    • 4

    解释:
    《1》point和Point2D是两个名称不同的类
    《2》变量p的类型被显示标注为Point类型 但是 它的值却是Point2D的实例 并且没有类型错误
    《3》因为TS是结构化类型系统 只检查Point和Point2D的结构是否相同(相同 都具有x和y两个属性 属性类型也相同)
    《4》但是 如果在Nominal Type System中 (比如C# Java等)它们是不同的类 类型无法兼容


    三十二、对象之间的兼容性

    注意:在结构化类型系统中,如果两个对象具有相同的形状 则认为它们属于同一类型 这种说法并不准确
    更准确地说 对于对象类型来说 y的成员至少与x相同 则x兼容y (成员多的可以赋值给少的)

    class Point {x:number | undefined ; y:number | undefined}
    class Point3D {x:number | undefined ; y:number | undefined ; z:number | undefined}
    
    
    • 1
    • 2
    • 3

    解释:
    《1》Point3D的成员至少与Point相同 则Point兼容Point3D
    《2》所以 成员多的Point3D可以赋值给成员少的Point


    三十三、类型兼容性

    除了class之外 TS中的其他类型也存在相互兼容的情况
    包括:

    《1》接口兼容性

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

    interface Point {
        x: number
        y: number
    }
    
    interface Point2D {
        x: number
        y: number
    }
    
    interface Point3D {
        x: number
        y: number
    }
    
    let p1: Point
    let p2: Point2D
    let p3: Point3D
    
    //正确:
    // p1 = p2
    // p2 = p1
    // p1 = p3
    
    // 错误演示:
    // p3 = p1
    
    class Point3D {x:number ; y:number ; z:number}
    let p3:Point2D = new Point3D()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    《2》函数兼容性

    函数之间的兼容性比较复杂 需要考虑
    1》参数个数 参数多的兼容参数少的 或者说 参数少的可以赋值给多的

       type F1 = (a:number) 	=> void
        type F2 = (a:number,b:number) => void
    
        let f1 : F1
        let f2 : F2
    
        // 正确
        f2 = f1
        // 错误示范 不能将类型“F2”分配给类型“F1”。
        f1 = f2
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

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

    2》参数类型

    type F1 = (a:number)=>string
    type F2 = (a:number)=>string
    let f1: F1
    let f2: F2 = f1
    
    • 1
    • 2
    • 3
    • 4

    解释:函数类型F2兼容函数类型F1 因为F1和F2的第一个参数类型相同

    参数类型复杂的情况

    interface Point2D {x:number ; y:number}
    interface Point3D {x:number ; y:number ; z:number}
    type F2 = (p: Point2D)=> void
    type F3 = (p: Point3D)=> void
    let f2:F2
    let f3:F3 = f2
    f2=f3
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    解释:
    1.此处与前面讲到的接口兼容性冲突
    2.技巧:将对象拆开 把每个属性看作一个个参数 则参数少的可以赋值给参数多的
    3》返回值类型
    只关注返回值类型本身即可

    type F5 = () => string
    type F6 = () = > string
    let f5 : F5
    let f6 : F6 = f5
    
    //
    type F7 = ()=>{name:string}
    type F8 = () =>{name:string ; age:number}
    let f7 : F7
    let f8 : F8
    f7 = f8
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    解释:
    1》如果返回值类型是原始类型 此时两个类型要相同 比如 左侧类型是F5和F6
    2》如果返回值类型是对象类型 此时成员多的可以赋值给成员少的 比如 右侧类型F7和F8

    交叉类型
    类似于之前的接口继承
    功能类似于接口继承(extends)用于组合多个类型为一个类型(常用于对象类型)
    比如:

    interface Person { name: string }
    interface Cotact { phone: string }
    
    type PersonDetail = Person & Cotact
    
    let obj: PersonDetail = {
        name: 'Jack',
        phone: '12'
    }
    
    console.log(obj)
    //{ name: 'Jack', phone: '12' }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    解释:
    使用交叉类型后 新的类型PersonDetail就同时具备了Person和Contact的所有属性类型
    相当于:
    type PersonDetail = {name:string; phone:string}


    三十四、交叉类型和接口之间的对比

    相同点:都可以实现对象类型的组合
    不同点:两种方式实现类型组合时 对于同名属性之间 处理类型冲突的方式不同

    interface A {
        fn: (value: number) => string
    }
    interface B extends A {
        fn: (value: string) => string
    }
    
    type C = A & B
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    说明 以上代码 接口继承会报错(类型不兼容)
    交叉类型没有错误 可以简单理解为
    fn : (value : number } string) => string


    三十五、泛型的使用

    泛型是可以在保证类型安全前提下 让函数等与多种类型一起工作 从而实现复用 常常用于 函数 接口 class中
    需求:创建一个id函数 传入什么数据就返回该数据本身 (也就是说 参数和返回值类型相同)

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

    比如 id(10)调用以上函数就会直接返回10本身 但是该函数只接收数值类型 无法用于其他类型
    但是也不能够加any 便会失去ts的保护机制

    function id<Type>(value:Type):Type {return value}
    
    • 1

    创建泛型函数:

    《1》语法 在函数名称的后面添加<>(尖括号)尖括号中添加类型变量 比如此处的Type
    《2》类型变量Type 是一种特殊类型的变量 它处理类型而不是值
    《3》该类型变量相当于一个类型容器 能够捕获用户提供的类型(具体是什么类型
    由用户调用该函数时指定)
    《4》因为Type是类型 因此可以将其作为函数参数和返回值的类型 表示参数和返回值具有相同的类型
    《5》类型变量Type 可以是任意合法的变量名称

    function id<Type>(value: Type): Type { return value }
    
    const id_1 = id<number>(1)
    const id_2 = id<string>('123')
    
    console.log(id_1, id_2)
    //1 123
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    解释:
    《1》语法:当函数名称的后面添加<>(尖括号)尖括号中指定具体的类型 比如 此处的number
    《2》当传入类型number后 这个类型就会被函数声明时指定的类型变量Type捕获到
    《3》此时 Type的类型就是number 所以 函数id参数和返回值的类型也都是number
    同样的 如果传入类型string 函数id参数和返回值的类型都是string
    这样 通过泛型就做到了让id函数与多种不同的类型一起工作 实现了复用的同时保证了类型安全


    三十六、简化泛型函数调用

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

    推荐:使用这种简化的方式调用泛型函数 使代码更短 更易于阅读
    说明:当编译器无法推断类型或者推断的类型不准确时 就需要显示都传入类型参数


    三十七、泛型约束

    默认情况下 泛型函数的类型变量Type可以代表多个类型 这导致无法访问任何属性。
    比如 id(‘a’)调用函数时获取参数的长度:

    function id<Type>(value:Type):Type {
    console.log(value.length)
    return value
    }
    
    • 1
    • 2
    • 3
    • 4

    解释:
    Type可以代表任意类型 无法保证一定存在length属性 比如number类型就没有length。
    此时 就需要为泛型添加约束来收缩类型(缩窄类型取值范围)
    function id(value: Type): Type {
    console.log(value.length)
    return value
    }

    1》指定更加具体的类型

    function id<Type>(value: Type[]): Type[] {
        console.log(value.length)
        return value
    }
    比如 将类型修改为Type[] (Type类型的数组) 因为只要是数组就一定存在length属性
    因此便可以访问了
    //
    function id<Type>(value: Type[]): Type[] {
        console.log(value.length)
        return value
    }
    
    console.log(id([1, 2, 3, 4, 5]))
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    2》添加约束

    interface ILength { length: number }
    
    function id<Type extends ILength>(value: Type): Type {
        console.log(value.length)
        return value
    }
    
    console.log(id([1, 2, 3, 4, 5]))
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    解释:
    《1》创建描述约束的接口ILength 该接口要求提供length属性
    《2》通过extends关键字使用该接口 为泛型(类型变量)添加约束
    《3》该约束表示:传入的类型必须具有length属性
    注意:传入的实参(比如 数组)只要有length属性即可 这也符合前面讲到的接口的类型兼容属性。


    三十八、多个泛型变量的情况

    泛型变量可以有多个 并且类型变量之间还可以约束(比如 第二个类型受第一个类型变量的约束)
    比如 创建一个函数来获取对象中属性的值:

    function getProp<Type, Key extends keyof Type>(obj: Type, Key: Key) {
        return obj[Key]
    //返回key中所对应的值
    }
    
    let person1 = { name: 'jack', age: 18 }
    
    getProp(person1, 'name')
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

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


    三十九、泛型接口

    接口也可以配合泛型来使用 以增加其灵活性 增强其复用性

    interface IdFunc<Type>{
    id : (value:Type)=>Type
    ids:() = > Type[]
    }
    
    let obj : IdFunc<number> = {
    id(value) {return value},
    id(){return [1,3,5]}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    解释:
    <1>在接口名称的后面添加<类型变量>那么 这个接口就变成了泛型接口
    <2>接口的类型变量 对接口中所有其他成员可见 也就是接口中所有成员都可以使用类型变量
    <3>使用泛型接口时 需要显示指定具体的类型
    <4>此时 id方法的参数和返回值类型都是number ids方法的返回值类型是number[]

    数组是泛型接口
    实际上 JS中的数组在TS中就是一个泛型接口

    const strs = ['a', 'b', 'c']
    strs.forEach(item => {})
    //(method) Array.forEach(callbackfn: (value: string, index: number, array: string[]) => void, thisArg?: any): void
    const nums = [1, 3, 5]
    nums.forEach(item=>{})
    //(method) Array.forEach(callbackfn: (value: number, index: number, array: number[]) => void, thisArg?: any): void
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    解释:
    《1》当我们在使用数组时 TS会根据数组的不同类型 来自动将类型变量设置为相应的类型
    《2》技巧 可以通过Ctrl + 鼠标左键 来查看具体的类型信息


    四十、泛型类(class也可以配合泛型来使用)

    比如:React的class组件的基类Component就是泛型类 不同的组件有不同的props和state

    解释:
    React.Component泛型类两个类型变量 分别指定props和state类型
    interface IState { count: number }
    interface IProps { maxLength: number }

    class InputCpunt extends React.Component{
    state: IState = {
    count: 0
    }

    render() {
        return 
    { this.props.maxLength }
    }
    • 1
    • 2
    • 3

    }

    创建泛型类:

    class GenericNumber<NumType>{
        defaultValue: NumType | undefined
        add: ((x: NumType, y: NumType) => NumType) | undefined
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    解释:
    《1》类似于泛型接口 在class名称后面添加<类型变量>这个类就变成了泛型类
    《2》此处的add方法 采用的就是箭头函数形式的类型书写方式
    const myNum = new GenericNumber()
    console.log(myNum.defaultValue = 10)

    省略参数的写法

    class GenericNumber<NumType>{
        defaultValue: NumType | undefined
        add: ((x: NumType, y: NumType) => NumType) | undefined
    
        constructor(value: NumType) {
            this.defaultValue = value
        }
    }
    
    const myNum = new GenericNumber(100)
    console.log(myNum.defaultValue)
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    类似于泛型接口 在创建class实例时 在类名后面通过<类型>来指定明确的类型


    四十一、泛型工具类型

    TS内置了一些常用的工具类型 来简化TS中的一些常见操作
    说明:它们都是基于泛型实现的(泛型适用于多种类型 更加通用)并且是内置的 可以直接在代码中使用。
    主要学习以下几个

    《1》Partial

    泛型工具类型 Partial用来构造(创建)一个类型 将Type的所有属性设置为可选。

    interface Props {
        id: string
        children: number[]
    }
    
    type PartialProps = Partial<Props>
    
    //Partial
    type PartialProps = {
        id?: string | undefined;
        children?: number[] | undefined;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    调用:

    let p1: Props = {
        id: "1",
        children: [1, 2, 3, 4]
    }
    
    let p2: PartialProps = {
        id: '123'
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    《2》Readonly

    用来构造一个类型 将Type的所有属性都设置为readonly(只读)

    interface Props {
        id: string
        children: number[]
    }
    
    type PartialProps = Readonly<Props>
    
    let p1: PartialProps = {
        id: "1",
        children: [1, 2, 3, 4]
    }
    //无法分配到 "id" ,因为它是只读属性。
    p1.id = '3'
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    解释:当我们重新给id属性赋值时 就会报错 无法分配到“id”因为它是只读属性

    《3》Pick

    《4》Record

    type RecordObj = Record<'a' | 'b' | 'c', string[]>
    
    // type RecordObj = {
    //     a: string[]
    //     b: string[]
    //     c: string[]
    // }
    
    
    let obj: RecordObj = {
        a: ['1'],
        b: ['2'],
        c: ['3']
    }
    
    console.log(obj)
    //{ a: [ '1' ], b: [ '2' ], c: [ '3' ] }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    解释:
    《1》Record工具类型有两个类型变量
    1.表示对象有哪些属性
    2.表示对象属性的类型
    《2》构建的新对象类型RecordObj表示:这个对象有三个属性分别为a/b/c 属性值的类型都是string[]

    四十二、索引签名类型

    绝大多数情况下 我们都可以在使用对象前就确定对象的结构 并为对象添加准确的类型
    使用场景:当无法确定对象中有哪些属性(或者说对象中可以出现任意多个属性)此时 就用到索引签名类型了

    interface AnyObject {
        [key: string]: number
    }
    
    let obj: AnyObject = {
        a: 1,
        abc: 124,
        abde: 12345
    }
    //{ a: 1, abc: 124, abde: 12345 }
    console.log(obj)
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    解释:
    《1》使用[key:string]来约束该接口中允许出现的属性名称 表示只要是string类型的属性名称 都可以出现在对象中
    《2》这样 对象obj中就可以出现任意多个属性(比如 a b等等)
    《3》key 只是一个占位符 可以换成任意合法的变量名称
    《4》隐藏的前置知识 JS中对象 ({})的键是string类型的
    在js中数组是一类特殊的对象 特殊在数组的键(索引)是数值类型
    并且 数组也可以出现任意多个元素 所以在数组对应的泛型接口中 也用到了索引签名类型
    解释:
    《1》MyArray接口 模拟原生的数组接口 并使用[n:number]来作为索引签名类型。
    《2》该索引签名类型表示:只要是number类型的键(索引)都可以出现在数组中 或者说数组中可以有任意多个元素
    《3》同时也符合数组索引是number类型这一前提

    interface MyArray<T> {
        [n: number]: T
    }
    
    let arrs: MyArray<number> = [1, 3, 5]
    
    //[ 1, 3, 5 ]
    console.log(arrs)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    四十三、映射类型

    映射类型:基本旧类型创建新类型 (对象类型),减少重复 提升开发效率
    比如:类型PropKeys有x/y/z 另一个类型Type1 中也有x/y/z 并且Type1中x/y/z的类型相同

    type PropKeys = 'x' | 'y' | 'z'
    type Type1 = {x:number; y:number;z:number}
    
    • 1
    • 2

    这样书写没错 但是x/y/z重复书写了两次 像这种情况 就可以使用映射类型来进行简化

    type PropKeys = 'x' | 'y' | 'z'
    type Type2 = {[Key in PropKeys]:number}
    
    • 1
    • 2

    解释:
    《1》映射类型是基础索引签名类型的 所以 该语法类似于索引签名类型 也使用了[]
    《2》Key in PropKeys 表示Key可以是PropKeys联合类型中的任意一个 类似于forin(let k in obj)
    《3》使用映射类型创建的新对象类型Type2和类型Type1结构完全相同
    《4》注意:映射类型只能在类型别名中使用 不能在接口中使用

    type PropKeys = 'x' | 'y' | 'z'
    type Type1 = { x: number; y: number; z: number }
    type Type2 = { [Key in PropKeys]: number }
    
    let obj1: Type1 = {
        x: 0,
        y: 0,
        z: 0
    }
    
    let obj2: Type2 = {
        x: 0,
        y: 0,
        z: 0
    }
    
    console.log(obj1)
    console.log(obj2)
    //
    { x: 0, y: 0, z: 0 }
    { x: 0, y: 0, z: 0 }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    四十四、映射类型

    映射类型
    除了根据联合类型创建新的类型外 还可以根据对象类型来创建

    type Props = {a:number;b:string;c:boolean}
    type Type3 = {[Key in keyof Props]:number}
    let obj: Type3 = {
        a: 0,
        b: 0,
        c: 0
    }
    
    console.log(obj)
    //{ a: 0, b: 0, c: 0 }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    使用[]来访问对象的属性 P是对象中的键
    获取T对象中键为P的值

    解释:
    《1》keyof T 即keyof Props表示获取Props的所有键 也就是:‘a’|‘b’|‘c’
    《2》在[]后面添加?(问号)表示将这些属性变为可选的 以此来实现Partial的功能
    《3》冒号后面的T[P]表示获取T中每个键对应的类型 比如 如果是’a’则类型是number 如果是’b’ 则类型是string
    《4》最终 新类型PartialProps和旧类型Props结构完全相同 只是让所有类型都变为可选了


    四十五、索引查询类型

    刚刚用到的T[P]语法 在TS中叫做索引查询(访问)类型
    作用:用来查询属性的类型

    type Props = { a: number; b: string; c: boolean }
    type TypeA = Props['a']
    
    • 1
    • 2

    解释:
    《1》Props[‘a’]表示查询类型Props中属性’a’对应的类型number 所以 TypeA的类型为number
    《2》[] 中的属性必须存在于被查询类型中 否则就会报错


    四十六、索引查询类型(同时查询多个)

    type Props = { a: number; b: string; c: boolean }
    // string | number
    // type TypeA = Props['a' | 'b']
    // string | number | boolean
    type TypeA = Props[keyof Props]
    
    • 1
    • 2
    • 3
    • 4
    • 5

    解释:
    《1》使用字符串字面量的联合类型 获取属性a和b对应的类型 结果为string | number
    《2》使用keyof操作符获取Props中所有键对应的类型 结果为string | number | boolean


    四十七、第三方库

    类型声明文件:用来为已存在的JS库提供类型信息
    《1》TS的两种文件类型
    《2》类型声明文件的使用说明


    四十八、TS中的两种文件类型

    1.ts文件 2 .d.ts文件

    .ts文件:
    《1》既包含类型信息又包含可执行代码
    《2》可以被编译为.js文件 然后 执行代码
    《3》用途:编写程序代码的地方

    .d.ts文件:
    《1》只包含类型信息的类型声明文件
    《2》不会生成.js代码 仅用于提供类型信息
    《3》用途:为JS提供类型信息
    总结:
    .ts是implementation(代码实现文件)
    .d.ts是declaration(类型声明文件)
    如果要为JS提供类型信息 要使用.d.ts文件

    在使用TS开发项目时 类型声明文件的使用包含以下两种方式

    《1》使用已有的类型声明文件
    《2》创建自己的类型声明文件

    学习顺序:先会用(别人的)再会写(自己的)

    《1》使用已有的类型声明文件
    *1.内置类型声明文件
    TS为JS运行时可用的所有标准化内置API都提供了声明文件。
    比如 在使用数组时 数组所有方法都会有相应的代码提示以及类型信息

    (method) Array<number>.forEach(callbackfn: (value: number, index: number, array: number[]) 
    => void, thisArg?: any): void
    
    • 1
    • 2

    实际上这都是TS提供的内置类型声明文件
    可以通过Ctrl + 鼠标左键来查看内置类型声明文件内容
    比如 查看forEach方法的类型声明 在VSCode中会自动跳转到lib.es5.d.ts类型声明文件中
    当然 像window document等BOM DOM API也都有相应的类型声明(lib.dom.d.ts)
    2.第三方库的类型声明文件
    《1》库自带类型声明文件
    比如 axios
    解释:在这种情况下 正常导入该库 TS就会自动加载自己的类型声明文件 以提供该库的类型声明
    《2》由DefinitelyTyped提供
    这是一个github仓库 用来提供高质量TypeScript类型声明
    可以通过npm/yarn来下载该仓库提供的TS类型声明包 这些包的名称格式为:@types/

    比如 @types/react @types/lodash
    说明:在实际开发时 如果你使用的第三方库没有自带的声明文件 VSCode会给出明确的提示


    解释:
    《1》当安装@types/*类型声明后 TS也会自动加载该类声明包 以提供该库的类型声明
    《2》补充:TS官方提供了一个页面 可以来查询@types/*库

    第一步
    在该链接中找到命令:
    https://www.typescriptlang.org/dt/search?search=lodash
    
    npm i lodash
    npm i @types/lodash --save-dev
    
    第一个是下载库的
    第二个是下载声明文件的
    
    在终端仅执行第一个指令后 在index.ts文件中引入
    import _ from 'lodash'
    报错:
    无法找到模块“lodash”的声明文件。“D:/codeFiles/VsCodeProjects/vscode-typescript/node_modules/lodash/lodash.js”隐式拥有 "any" 类型。
      尝试使用 `npm i --save-dev @types/lodash` (如果存在),或者添加一个包含 `declare module 'lodash';` 的新声明(.d.ts)文件ts(7016)
    
    其它范例:
    npm i react
    npm i @types/react --save-dev
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    《2》创建自己的类型声明文件

    1. 项目内共享类型

    2. 为已有JS文件提供类型声明

    3. 项目内共享类型

    如果多个.ts文件中都用到同一个类型 此时可以创建.d.ts文件提供该类型 实现类型共享
    操作步骤:
    《1》创建index.d.ts 类型声明文件
    《2》创建需要共享的类型 并使用export导出(TS中的类型也可以使用import/export实现模块化功能)
    《3》在需要使用共享类型的.ts文件中 通过import导入即可(.d.ts后缀导入时 直接省略)

    2.为已有JS文件提供类型声明

    2.1 在将JS项目迁移到TS项目时 为了让已有的.js文件有类型声明。
    2.2 成为库作者 创建库给其他人使用

    注意: 类型声明文件的编写与模块化方式相关 不同的模块化方式有不同的写法 但由于历史原因 JS模块化的发展经历过多种变化(AMD CommonJS UMD ESModule等) 而TS支持各种模块化形式的类型声明 这就导致类型声明文件相关内容又多又杂

    演示:
    基于最新的ESModule (import/export)来为已有.js文件 创建类型声明文件。
    开发环境准备:使用webpack搭建 通过ts-loader处理.ts文件

    说明:TS项目中也可以使用.js文件
    说明:在导入.js文件时 TS会自动加载与.js同名的.d.ts文件 以提供类型声明
    declare关键字:用于类型声明 为其它地方(比如.js文件)已存在的变量声明类型 而不是创建一个新的变量
    《1》对于type interface 等这些明确就是TS类型的(只能在TS中使用的)可以省略declare关键字
    《2》对于let function 等具有双重含义(在JS TS中都能用)应该使用declare关键字 明确指出此处使用declare关键字 明确指定此处用于类型声明


    四十九、在React中使用TypeScript概述

    在前端项目开发中使用TS 还需要掌握React Vue Angular等这些库或框架中提供的API的类型 以及在TS中是如何使用的

    接下来 以React为例 来学习如何在React项目中使用TS 包括以下内容:

    《1》使用CRA创建支持TS的项目

    React脚手架工具create-react-app(简称:CRA)默认支持TypeScript
    创建支持TS的项目命令:npx create-react-app 项目名称 --template typescript
    当看到以下提示时 表示支持TS的项目创建成功
    We suggest that you begin by typing:
    cd react-ts-basic
    npm start


    五十、React支持TS的项目目录结构

    相对于非TS项目 目录结构主要由以下三个变化
    1.项目根目录中增加了tsconfig.json配置文件 指定TS的编译选项 (比如 编译时 是否移除注释)
    2.React组件的文件扩展名变为:*.tsx
    3.src目录中增加了react-app-env.d.ts:React项目默认的类型声明文件。
    
    react-app-env.d.ts:React项目默认的类型声明文件
    三斜线指令 : 指定依赖的其它类型声明文件 types表示依赖的类型声明文件包的名称
    /// 
    解释:
    告诉TS帮我加载react-scripts这个包提供的类型声明。
    react-scripts的类型声明文件包含了两部分类型
    《1》react react-dom node的类型
    《2》图片 样式等模块的类型 以允许在代码中导入图片 SVG等文件。
    
    TS会自动加载该.d.ts文件 以提供类型声明(通过修改tsconfig.json中的include配置来验证)
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    五十一、TS配置文件tsconfig.json

    指定项目文件和项目编译所需的配置项
    注意:TS的配置项非常多(100+)以CRA项目中的配置为例来学习 其它的配置项用到时查文档即可
    《1》tsconfig.json文件所在目录为项目根目录(与package.json同级)
    《2》tsconfig.json 可以自动生成 命令 tsc-init


    五十二、通过命令行方式使用编译配置

    使用演示:tsc hello.ts --target es6
    注意:
    1. tsc 后带有输入文件时(比如 tsc hello.ts)将忽略tsconfig.json
    2. tsc 后不带输入文件时 (比如 tsc)才会启用tsconfig.json
    推荐使用 : tsconfig.json 配置文件
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    五十三、React中的常用类型

    前提说明:现在 基于class组件来讲解React+TS的使用 (最新的React Hooks 在后面讲解)
    在不使用TS时 可以使用prop-types库 为React组件提供类型检查
    说明:TS项目中 推荐使用TypeScript实现组件类型校验(代替PropTypes)
    不管是React还是Vue 只要是支持TS的库 都提供了很多类型 来满足该库对类型的需求
    注意:
    1.React项目是通过 @types/react @types/react-dom 类型声明包 来提供类型的
    2.这些包 CRA已帮我们安装好(react-app-env.d.ts)直接用即可


    五十四、React函数组件的类型(1组件和属性类型)

    React是组件化开发模式 React开发主要任务就是写组件 
    两种组件:
    《1》函数组件
    1.组件的类型
    2.组件的属性(props)
    3.组件属性的默认值(defaultProps)
    4.事件绑定和事件对象
    
    type Props = {name:string;age?:number}
    const Hello:FC <Props> = ({name,age})=>(
    <div>你好,我叫:{name},{age} 岁了</div>
    )
    <Hello name="jack"/>
    实际上 还可以直接简化为 (完全按照函数在TS中的写法)
    const Hello = ({name,age}: Props) = >(
    <div>你好,我叫:{name},{age} 岁了</div>
    )
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    五十五、React函数组件的类型(2属性默认值)

    函数组件属性的默认值(defaultProps)
    const Hello:FC<Props> = ({name,age}) =>(
    <div>你好,我叫:{name},我{age}岁了</div>
    )
    Hello.defaultProps = {
    age: 18
    }
    实际上 还可以直接简化为(完全按照函数在TS中的写法)
    const Hello = ({name,age=18}:Props) =>(
    <div>你好,我叫:{name},我{age}岁了</div>
    )
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    五十六、React函数组件的类型(3事件和事件对象)

    事件绑定和事件对象
    <button onClick={onClick}>点赞</button>
    const onClick = () =>{}
    const onClick1=(e:React.MouseEvent<HTMLButtonElement>) =>{}
    再比如 文本框
    <input onChange={onChange}/>
    const onChange = (e:React.ChangeEvent<HTMLInputElement>) =>{}
    技巧:在JSX中写事件处理程序(e=>{})然后 把鼠标放在e上 利用TS的类型推论来查看事件对象类型
    <input onChange={e=>{}}>
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    五十七、React类组件的类型(1组件类型)

    2》class组件 主要包含以下内容:
    1.组件的类型 属性 事件
    type State = {count:number}
    type Props ={message?:string}
    
    class C1 extends React.Component {}  //无props state
    class C2 extends React.Component<Props> {} //有props 无state
    class C3 extends React.Component <{},State>{}//无props 有state
    class C4 extends React.Component <Props,State>{}//有props state
    
    2.组件状态(state)
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    五十八、React类组件的类型(2组件属性)

    class组件的属性和属性默认值
    type Props = {name:string;age?:number}
    class Hello extends React.Component<Props>{
       static defaultProps:Partial<Props> = {
    	age:18
    }
    
    render(){
    	const {name,age} = this.props
    	return <div>你好,我叫:{name},我{age}岁了</div>
        }
    }
    
    const {name,age=18} = this.props
    
    //解构
    const {name,age} = this.props
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    五十九、React类组件的类型(3状态和事件)

    class组件状态(state)和事件
    type State = {count:number}
    class Counter extends React.Component<{},State>{
    	state:State = {
    	count : 0
    }
    onIncrement=() =>{
    	this.setState({
    	count:this.state.count+1
    })
    }
    }
    
    <button onClick={this.onIncrement}>+1</button>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    六十、任务列表案例介绍

    TS+React实现todos案例
    功能演示:
    《1》展示任务列表
    《2》添加任务
    
    父组件:App
    子组件:TodoAdd TodoList
    
    展示任务列表:
    思路:使用状态提升(为父组件提供状态 通过props传递给子组件)来实现父->子通讯
    
    步骤:
    《1》为父组件App 提供状态(任务列表数据)和类型
    《2》为子组件TodoList指定能够接收到的props类型
    《3》将任务列表数据传递给TodoList组件
    
    //后面没必要看了
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
  • 相关阅读:
    Docker 与 Linux Cgroups:资源隔离的魔法之旅
    【AAAI2023】视觉辅助的常识知识获取Visually Grounded Commonsense Knowledge Acquisition 个人学习笔记
    计算机视觉40例之案例06缺陷检测
    实战PyQt5: 155-QChart图表之极坐标图表
    Codeforces 1281F 树上背包
    vue自定义指令来控制按钮权限
    Kafka 开发架构的一些问题汇总
    美国出台新规对全球芯片业产生深远影响 | 百能云芯
    好看的风筝
    React 全栈体系(九)
  • 原文地址:https://blog.csdn.net/Lixu_No_1/article/details/126191474