当前位置: 首页 > article >正文

TypeScript 快速上手 ------- 学习笔记分享

目录

一. TypeScript简介

二.为何需要TypeScript

三.编译TypeScript

1. 命令行编译

2. 自动化编译

四. 类型声明

五. 类型推断

六. 类型总览

6.1 JS中的数据类型

6.2 TS中的数据类型

6.3 注意点!

1. 原始类型 VS 包装对象

七. 常用类型

7.1 any

7.2 unknow

7.3 never

7.4 void

7.5 object

7.6 tuple(不是关键字)

7.7 enum

7.8 type

7.9 一个特殊情况

7.10 复习类相关知识

7.11 属性修饰符

7.12 抽象类

7.13 interface (接口)

7.14 一些相似概念的区别

八. 泛型

九. 类型声明文件


一. TypeScript简介
  1. TypeScript由微软开发,是基于Javascript的一个拓展语言

  2. TypeScript包含了JavaScript的所有内容,即前者是后者的超集

  3. TypeScript增加了:静态类型检查 \ 接口 \ 泛型等很多现代开发特性,因此更适合大型项目的开发

  4. TypeScript需要编译为JavaScript,然后交给浏览器或者其他JavaScript运行环境执行

二.为何需要TypeScript

TS能静态类型检查 : 简而言之就是把运行时的错误前置,便于后期代码的维护

三.编译TypeScript

浏览器不能直接运行TS的代码,需要编译为JS再交给浏览器解析器执行

1. 命令行编译
  • 第一步 : 创建一个demo.ts文件

  • 第二步 : 全局安装TypeScript

    npm i typescript -g
  • 第三步 : 使用命令行编译.ts文件

    tsc demo.ts
2. 自动化编译
  • 第一步 : 创建TypeScript编译控制文件

    tsc --init

    1.工程中会生成一个tsconfig.json配置文件,其中包含很多编译时的配置

    2.观察发现,默认编译的JS版本是ES7,我们可以手动调正为其他版本

  • 第二步 : 监视目录中的.ts文件变化

    tsc --watch
  • 第三步 : 小优化,当编译出错时不生成.js文件

    tsc --noEmitOnError --watch

    当然也可以修改tsconfig.json中的noEmitOnError配置

四. 类型声明

使用:来对变量或函数形参,进行类型声明:

let a: string
let b: number
let c: boolean
​
a = 'hello'
b = -99
c = true
console.log(a,b,c)
​
function count(x: number,y:number):number{
    return x + y
}
​
let result = count(1,2)
console.log(result)
​
// count(1)
// count(1,2)
​
let d :'hello'//字面量类型
五. 类型推断

TS会根据我们的代码,进行类型推导,例如下面代码中的变量d,只能存储数字

let d = -99
d = false//警告,不能将boolean分配给类型number

但要注意,类型推断不是万能的,面对复杂类型时推断容易出问题,所以尽量还是明确的编写类型声明!

六. 类型总览
6.1 JS中的数据类型
  • string

  • number

  • boolean

  • null

  • undefined

  • bigint

  • symbol

  • object(有子类型)

备注 : 其中object包含:Array,Function,Date,Error等...

6.2 TS中的数据类型
  • 上述所有JS类型

  • 六个新类型

    • any

    • unknow

    • never

    • void

    • tuple

    • enum

  • 两个用于自定义类型的方式

    • type

    • interface

6.3 注意点!

在JS中的这些内置构造函数:Number,String,Boolean,它们用于创建对应的包装对象,在日常开发时很少使用,在TS中也是同理,所以在TS中进行类型声明时,通常都是用小写number,string,boolean

1. 原始类型 VS 包装对象
  • 原始类型 : 如number,string,boolean,在JS中是简单数据类型,它们在内存中占用空间少,处理速度快

  • 包装对象 : 如Number对象,String对象,Boolean对象,是复杂类型,在内存中占用更多空间,在日常开发时很少有开发人员自己创建包装对象

  • 自动装箱 : JS在必要时会自动将原始类型包装成对象,一边调用方法或访问属性

七. 常用类型
7.1 any

any的含义是 : 任意类型,一旦将变量类型限制为any,那就意味着放弃了对该变量的类型检查

注意点 : any类型的变量,可以赋值给任意类型的变量

7.2 unknow

unknow的含义是 : 未知类型

  • unknow可以理解为一个类型安全的any,适用于 : 不确定数据的具体类型

  • unknow会强制开发者在使用之前进行类型检查,从而提供更强的类型安全性

    et a: unknown
    ​
    a = 99
    a = false
    a = 'hello'
    ​
    console.log(a)
    ​
    let x: string
    ​
    //第一种
    if(typeof a === 'string'){
        x = a
    }
    ​
    //第二种 (断言)
    x = a as string
    ​
    //第三种 (断言)
    x = <string>a
  • 读取any类型数据的任何属性都不会报错,而unknow正好与之相反

(str3 as string) ].toUpperCase()
7.3 never

never的含义是 : 任何值都不是,简而言之就是不能有值,undefined,null,'',0都不行 !

  • 几乎不用never去直接限制变量,因为没有意义,主要是用来限制函数返回类型

  • never一般是TS主动推断出来的

  • never也可用于限制函数的返回值

7.4 void
  • void通常用于函数返回值声明,含义 : [函数返回值为"空",调用者也不应依赖其返回值进行任何操作]

    function logMessage(msg:string):void{
        console.log(msg)
    }
    logMessage('你好')

    注意 : 编码者没有编写return去指定函数的返回值,所以logMessage函数是没有显式返回值的,但会有一个隐式返回值,就是undefined;即 : 虽然函数返回类型为void,但也是可以接受undefined的,简单记 : undefinedvoid可以接受的一种"空"

  • 以下写法均符合规范

    function logMessage1(msg:string):void{
        console.log(msg)
    }
    ​
    function logMessage2(msg:string):void{
        console.log(msg)
        return;
    }
    ​
    function logMessage3(msg:string):void{
        console.log(msg)
        return undefined
    }
  • 但限制函数返回值时,undefinedvoid还是有区别:[返回值类型为的void的函数,调用者不应依赖其返回值进行任何操作!]

理解void 与 undefined :

  • void 是一个广泛的概念,用来表达"空",而undefined则是这种"空"的具体实现之一

  • 因此可以说undefinedvoid能接受的"空"状态的一种具体形式

  • 换句话说 : void包含undefined,但void表达的语义超越了单纯的undefined,它是一种意图上的约定,而不仅仅是特定值的限制

总结 : 若函数返回类型为void,那么 :

  • 从语法上讲,函数是可以返回undefined的,至于显式返回,还是隐式返回,都无所谓

  • 从语义上讲,函数调用者不应关心函数返回的值,也不应依赖返回值进行任何操作!即使返回了undefined

7.5 object

关于objectObject,直接说结论,实际开发中用的相对较少,因为范围太大了

  • object(小写)

    object的含义是 : 所有非原始类型,可储存 : 对象 , 函数 , 数组等,由于限制的范围比较宽泛,在实际开发中使用的相对较少

  • Object(大写)

    • 官方描述 : 所有可以调用Object方法的类型

    • 简单记忆 : 除了undefinednull的任何值

    • 由于限制的范围实在太大了!所以实际开发中使用频率极低

  • 声明对象类型

1.实际开发中,限制一般对象,通常使用以下形式

//限制person对象必须有name属性,age为可选属性
let person: { name: string, age?: number }
​
//含义同上,也能使用分号进行分隔
let person2: { name: string; age?: number }
​
//含义同上,也可以使用换行分隔
let person3: {
    name: string
    age?: number
}
  1. 索引签名 : 允许定义对象可以具有任意数量的属性,这些属性的键和类型是可变的,常用于 : 描述类型不确定的属性,(具有动态属性的对象)

//限制person对象必须有name属性,可选age属性但必须是数字,同时可以有任意数量的任意类型
let person: {
    name: string
    age?: number
    [key: string]: any  //索引签名,完全可以不用key这个单词,换成其他的也可以,但key较为常用
}
  • 声明函数类型

let count: (a: number, b: number) => number
​
count = function (x, y) {
    return x + y
}
  • TS中的=>在函数类型声明时表示函数类型,描述其参数类型返回类型

  • JS中的=>是一种定义函 数的语法,是具体的函数实现

  • 函数类型声明还可以使用 : 接口,自定义类型等方式

  • 声明数组类型

let arr1: string[]
let arr2: Array<number>
​
arr1 = ['a','b','c','d']
arr2 = [100,200]

上述代码的Array<string>属于泛型

7.6 tuple(不是关键字)

元组(Tuple)是一种特殊的数组类型,可以存储固定数量的元素,并且每个元素的类型是已知的可以不同,元组用于精确描述一组值的类型,?表示可选元素

  //第一个元素必须是string类型,第二个元素必须是number类型
  let arr1: [string,number]
  //第一个元素必须是number类型,第二个元素是可选类型,如果存在,必须是boolean类型
  let arr2: [number,boolean?]
  //第一个元素必须是number类型,后面的元素可以是任意数量的string类型
  let arr3: [number,...string[]]
​
  //可以赋值
  arr1 = ['hello',123]
  arr2 = [100,false]
  arr3 = [100,'hello','world']
7.7 enum

枚举(enum)可以定义一组命名常量,它能增强代码的可读性,也让代码更好维护

如下代码的功能是 : 根据调用walk时传入的不同参数,执行不同的逻辑,存在的问题是调用walk时传参时没有任何提示,编码者很容易写错字符串内容;并且用于判断逻辑的up,down,left,right连续且相关的一组值,那此时就特别适合使用枚举

function walk(str: string) {
    if (str === 'up') {
        console.log("向[上]走");
    } else if (str === 'down') {
        console.log('向[下]走');
    } else if (str === 'left') {
        console.log('向[左]走');
    } else if (str === 'right') {
        console.log('向[右]走');
    } else {
        console.log('未知方向');
    }
}
​
walk('up')
walk('down')
walk('left')
walk('right')
  • 数字枚举

数字枚举是一种最常见的枚举类型,其成员的值会自动递增,且数字枚举还具备反向映射的特点,在下面代码的打印中,不难发现 : 可以通过来获取对应的枚举成员名称

enum Direction {
    Up,
    Down,
    Left,
    Right
}
​
console.log(Direction.Up);//打印0
console.log(Direction[0]);//打印Up
​
function walk(data:Direction) {
    console.log(data);
    if (data === Direction.Up) {
        console.log("向[上]走");
    } else if (data === Direction.Down) {
        console.log('向[下]走');
    } else if (data === Direction.Left) {
        console.log('向[左]走');
    } else if (data === Direction.Right) {
        console.log('向[右]走');
    } else {
        console.log('未知方向');
    }
}
​
walk(Direction.Down)

也可以指定枚举成员的初始值,其后的成员值会自动递增

enum Direction {
    Up = 5,
    Down,
    Left,
    Right
}

使用数字枚举完成刚才walk函数中的逻辑,此时我们发现 : 代码更加直观易读,而且类型安全,同时也更易于维护

  • 字符串枚举

枚举成员的值是字符串

enum Direction {
    Up = "Up",
    Down = "Down",
    Left = "Left",
    Right = "Right"
}
​
let dir:Direction = Direction.Up
console.log(dir);//输出Up

字符串枚举就丢失了反向映射

  • 常量枚举

官方描述 : 常量枚举是一种特殊的枚举类型,它使用const关键字定义,在编译时会被内联,避免生成一些额外的代码

何为编译时内联?

所谓"内联",其实就是TS在编译时,会将枚举成员引用替换为它们的实际值,而不是生成额外的枚举对象,这还可以减少生成的JS代码量,并提高运行时性能

常量枚举的TS代码如下:

编译后生成的JS代码如下:(代码量较小)

7.8 type

type可以为任意类型创建别名,让代码更简洁,可读性更强,同时能更方便地进行类型复用和扩展

  • 基本用法

    类型别名使用type关键字定义,type后跟类型名称,例如下面代码中num是类型别名

    type num = number;
    let price: num
    price = 100
  • 联合类型

    联合类型是一种高级类型,它表示一个值可以是几种不同类型之一

    type Gender = '男' | '女'
    type Status = number | string
    function printStatus(status: Status){
        console.log(status)
    }
    function logGender(str: Gender){
        console.log(str)
    }
  • 交叉类型

    交叉类型允许将多个类型合并为一个类型;合并后的类型将拥有所有被合并类型的成员;交叉类型通常用于对象类型

    //面积
    type Area = {
        height: number;//高
        width: number;//宽
    };
    ​
    //地址
    type Address = {
        num: number//楼号
        cell: number//单元号
        room: string//房间号
    }
    ​
    //
    type House = Area & Address
    ​
    const house: House = {
        height: 100,//高
        width: 100,//宽
        num: 1,//楼号
        cell: 4,//单元号
        room: '999'//房间号
    }
7.9 一个特殊情况

先来观察如下两段代码 :

代码段1(正常)

在函数定义时,限制函数返回值为void,那么函数的返回值就必须是空

function demo(){
    //返回undefined合法
    return undefined
    
    //以下返回均不合法
    return 100
    return false 
    return null
    return []
}

代码段2(特殊)

使用类型声明限制函数返回值为void时,TS并不会严格要求函数返回空

type LogFunc = () => void
​
const f1:LogFunc = function(){
    return 66
}
const f2:LogFunc = function(){
    return []
}
const f3:LogFunc = function(){
    return null
}
const f4:LogFunc = function(){
    return false
}

为什么会这样?

官网解释:是为了确保如下代码成立,我们知道Array.prototype.push返回一个数字,而Array.prototype.forEach方法期望其回调的返回类型是void

const src = [1,2,3];
const dst = [0];
​
src.forEach((el) => dst.push(el));

说白了,就是为了避免与"箭头函数只有一句的时候可以简写"发生冲突(做了一个取舍)

7.10 复习类相关知识
class Person {//复习类
    name: string
    age: number
    constructor(name: string, age: number) {
        this.name = name
        this.age = age
    }
    speak() {
        console.log(`我叫:${this.name},今年${this.age}岁`);
    }
}
​
// const p1 = new Person('张三',18)
// console.log(p1);
// p1.speak()
​
class Student extends Person {
    grade: string
    constructor(name: string, age: number, grade: string) {
        super(name, age)
        this.grade = grade
    }
    study() {
        console.log(`${this.name}正在努力学习中......`);
    }
    override speak() { //写上overri能避免speak打错了造成的影响
        console.log(`我是学生,我叫:${this.name},今年${this.age}岁`);//覆盖父辈的speak内容
    }
}
​
const s1 = new Student('徐同学', 16,'高一')
s1.study()
console.log(s1);
s1.speak()//调用父辈方法(继承)
7.11 属性修饰符
修饰符含义具体规则
public公开的可以被:类内部 \ 子类 \ 类外部访问
protected受保护的可以被:类内部 \ 子类访问
private私有的可以被:类内部访问
readonly只读属性属性无法修改
  • public修饰符

    class Person {
        public name: string
        public age: number
        constructor(name: string, age: number) {
            this.name = name
            this.age = age
        }
        speak() {
            console.log(`我叫:${this.name},今年${this.age}岁`);//public修饰的属性在类的内部可以访问
        }
    }
    ​
    class Student extends Person {
        study() {
            console.log(`${this.name}正在努力学习中......`);//public修饰的属性在子类的内部也能访问
        }
    }
    ​
    //public修饰的属性在外部也能被访问
    const p1 = new Person('tom',18)
    p1.age
    p1.name
    p1.speak()
  • 属性的简写形式

    // class Person {
    //     public name: string
    //     public age: number
    //     constructor(name: string, age: number) {
    //         this.name = name
    //         this.age = age
    //     }
    // }//简写前代码
    ​
    class Person {
        constructor(
            public name: string,
            public age: number
            ) { }
    }//简写后的代码
    ​
  • protected修饰符

    class Person {
        //name和age是受保护属性,不能在类外部被访问,但可以在[类]与[子类]中访问
        constructor(
            protected name: string,
            protected age: number
        ) { }
        //getDetails是受保护方法,不能在类外部访问,但可以在[类]与[子类]中访问
        protected getDetails(): string{
            //类中能访问受保护的name和age属性
            return "我叫:${this.name},年龄是:${this.age}"
        }
        //introduce是公开方法,类 \ 子类 \ 类外部都能使用
        introduce(){
            //类中能访问受保护的getDetails方法
            console.log(this.getDetails());
        }
    }
    ​
    const p1 = new Person('阿巴',18)
    //可以在外部访问introduce
    p1.introduce()
    ​
    //受保护的属性是无法在外部被访问的
    // p1.name
    // p1.age
    // p1.getDetails
    ​
    //受保护的name可以在子类中被访问
    class Student extends Person {
        study(){
            this.introduce()
            //子类中可以访问受保护的name
            console.log(`${this.name}正在努力学习`);
        }
    }
    const s1 = new Student('tom',12)
    s1.study()
  • private修饰符

    class Person {
        //name和age是受保护属性,不能在类外部被访问,但可以在[类]与[子类]中访问
        constructor(
            protected name: string,
            protected age: number,
            //Incard属性为私有的(private)属性,只能在[类内部]使用
            private IDcard: string
        ) { }
        private getPrivateInfo(){
            //类内部可以访问私有的(private)属性 -- IDcard
            return `身份证号码为:${this.IDcard}`
        }
        getInfo() {
            //类内部可以访问受保护的(protected)属性
            return `我叫:${this.name},今年刚满${this.age}岁`
        }
        getFullInfo() {
            //类内部可以访问公开的getInfo方法,也可以访问私有的getPrivateInfo方法
            return this.getInfo() + ', ' + this.getPrivateInfo()
        }
    }
    ​
    ​
    const p1 = new Person('tom',18,'1111111111111111')
    //在类外部无法被访问
    // p1.name
    // p1.age
    // p1.IDcard
    console.log(p1.getInfo());
    console.log(p1.getFullInfo());
  • readonly修饰符

    class Person {
        constructor(
            public readonly name: string,
            public age: number,
        ) { }
    }
    ​
    const p1 = new Person('tom',18)
    console.log(p1);
    p1.age = 20
    console.log(p1);//age可以改
    // p1.name = '西巴' //name被readonly修饰后不能被修改,只能被读取
7.12 抽象类
  • 概念 : 抽象类是一种无法被实例化的类,专门用来定义类的结构和行为,类中可以写抽象方法,也可以写具体实现.抽象类主要用来为其派生类提供一个基础结构,要求其派生类必须实现其中的抽象方法

  • 简记 : 抽象类不能实例化,其意义是可以被继承,抽象类里可以有普通方法,也可以有抽象方法

通过以下场景,理解抽象类 :

我们定义一个抽象类Package,表示所有包裹的基本结构,任何包裹都有重量属性weight,包裹都需要计算运费.但不同类型的包裹 (如 : 标准速度 , 特快专递)都有不同的计算方式,因此用于计算运费的calculate方法是一个抽象方法,必须由具体的子类来实现.

  • Package类

    abstract class Package {
        //构造方法
        constructor(public weight: number) { }
        //抽象方法
        abstract calculate(): number
        //具体方法
        printPackage() {
            console.log(`包裹重量为: ${this.weight}kg,运费为:${this.calculate()}元`);
        }
    }

StandardPackage类继承了Package,实现了calculate方法 :

  • StandardPackage类 (标快包裹)

    abstract class Package {
        //构造方法
        constructor(public weight: number) { }
        //抽象方法
        abstract calculate(): number
        //具体方法
        printPackage() {
            console.log(`包裹重量为: ${this.weight}kg,运费为:${this.calculate()}元`);
        }
    }
    ​
    ​
    // const p1 = new Package(100)  抽象类是不能new的,但它可以被继承
    ​
    class StandardPackage extends Package {
        constructor(
            weight: number,
            public unitPrice: number
        ) { super(weight) }//继承父类的weight
        calculate(): number {
            return this.weight * this.unitPrice;
        }
    }
    ​
    const s1 = new StandardPackage(10,5)
    s1.printPackage()

ExpressPackage类继承了Package,实现了calculateAmout方法:

  • ExpressPackage类 (特快包裹)

    abstract class Package {
        //构造方法
        constructor(public weight: number) { }
        //抽象方法
        abstract calculate(): number
        //具体方法
        printPackage() {
            console.log(`包裹重量为: ${this.weight}kg,运费为:${this.calculate()}元`);
        }
    }
    ​
    ​
    // const p1 = new Package(100)  抽象类是不能new的,但它可以被继承
    ​
    class StandardPackage extends Package {
        constructor(
            weight: number,
            public unitPrice: number
        ) { super(weight) }//继承父类的weight
        calculate(): number {
            return this.weight * this.unitPrice;
        }
    }
    ​
    const s1 = new StandardPackage(10,5)
    s1.printPackage()
    ​
    class ExpressPackage extends Package {
        constructor(
            weight: number,
            public unitPrice: number,
            public additional: number,
        ) { super(weight) }//继承父类的weight
        calculate(): number {
            if(this.weight > 10){
                //超出10kg的部分,每公斤多收2元 
                return 10 * this.unitPrice + (this.weight - 10) * this.additional
            } else {
                return this.weight * this.unitPrice;
            }
        }
    }
    ​
    const e1 = new ExpressPackage(13,8,2)
    e1.printPackage()

总结 : 何时使用抽象类 ?

  1. 定义通用接口 : 为一组相关的类定义通用的行为 (方法或属性) 时.

  2. 提供基础实现 : 在抽象类中提供某些方法或者为其提供基础实现,这样派生类就可以继承这些实现.

  3. 确保关键实现 : 强制派生类实现一些关键行为.

  4. 共享代码和逻辑 : 当多个类需要共享部分代码时,抽象类可以避免代码重复

7.13 interface (接口)

interface是一种定义结构的方式,主要作用是为类 \ 对象 \ 函数等规定一种契约,这样可以确保代码的一致性和类型安全,但要注意interface只能定义格式,不能包含任何实现!

  • 定义类结构

    //PersonInterface接口
    interface PersonInterface {
        name:string
        age:number
        speak(n:number):void//方法
    ​
    }
    ​
    class Person implements PersonInterface{
        constructor(
            public name:string,
            public age:number,
        ){}
        speak(n: number): void {
            for (let i = 0; i < n; i++) {
                console.log(`你好,我叫${this.name},我的年龄是${this.age}岁`);   
            }
        }
    }
    ​
    ​
    const p1 =  new Person('tom',18)
    p1.speak(4)
  • 定义对象结构

    interface UserInterface {
        name: string
        readonly gender: string
        age?: number//可选属性
        run: (n: number) => void
    }
    ​
    const user: UserInterface = {
        name: '张三',
        gender: '男',
        age: 18,
        run(n) {
            console.log(`奔跑了${n}米`);
        }
    };
  • 定义函数结构

    interface CountInterface {
        (a: number, b: number): number;
    }
    ​
    const count: CountInterface = (x, y) => {
        return x + y
    }
  • 接口之间的继承

    一个interface继承另一个interface,从而实现代码的复用

    interface PersonInterface {
        name: string
        age: number
    }
    ​
    interface StudentInterface extends PersonInterface {
        grade: string
    }
    ​
    const stu: StudentInterface = {
        name: '张三',
        age: 18,
        grade: '高三'
    }
  • 接口自动合并(可重复定义)

    interface PersonInterface {
        name: string
        age: number
    }
    ​
    interface PersonInterface {
        gender: string
    }//接口可以重复定义
    ​
    const p:PersonInterface = {
        name:'tom',
        age:10,
        gender:'男'
    }

总结 : 何时使用接口 ?

  1. 定义对象的格式 : 描述数据模型 , API响应格式 , 配置对象......是开发中用的最多的场景

  2. 类的契约 : 规定一个类需要实现哪些属性和方法

  3. 自动合并 : 一般用于扩展第三方库的类型 , 这种特性在大型项目中可能会用到

7.14 一些相似概念的区别
  • interface 与 type 的区别

    • 相同点 : interfacetype都可以用于定义对象结构,两者在许多场景中是可以互换的

    • 不同点 :

      1. interface:更专注于定义对象和类的结构,支持继承 \ 合并

      2. type:可以定义类型别名,联合类型,交叉类型,但不支持继承和自动合并

    • interfacetype都可以定义对象结构

      //使用 interface 定义Person对象
      interface PersonInterface {
          name: string;
          age: number;
          speak(): void;
      }
      ​
      //使用 type 定义Person对象
      type PersonType = {
          name: string;
          age: number;
          speak(): void;
      };
      ​
      let p1: PersonInterface = {
          name: 'tom',
          age: 18,
          speak() {
              console.log(this.name);
          }
      }
      ​
      let p2: PersonType = {
          name: 'tim',
          age: 18,
          speak() {
              console.log(this.name);
          }
      }
    • interface可以继承,合并

      interface PersonInterface {
          name: string
          age: number
      }
      ​
      interface PersonInterface {
          speak: () => void//体现可以合并
      }
      ​
      interface StudentInterface extends PersonInterface {
          grade: string//体现可以继承
      }
      ​
      const student: StudentInterface = {
          name: '李四',
          age: 18,
          grade: '高二',
          speak() {
              console.log(this.name, this.age, this.grade);
          }
      }
    • type的交叉类型

      // interface PersonInterface {
      //     name: string
      //     age: number
      // }
      ​
      // interface PersonInterface {
      //     speak: () => void    //追加
      // }
      ​
      // interface StudentInterface extends PersonInterface {
      //     grade: string    //扩展
      // }
       
      //使用type
      type PersonType = {
          name: string;
          age: number;
      } & {
          speak:() => void;   //追加操作(交叉类型)
      };
      ​
      //使用type定义Student类型,并通过交叉类型继承 PersonType
      type StudentType = PersonType & {
          grade: string;
      };
  • interface 与 抽象类的区别

    • 相同点 : 都用于定义一个类的格式(应该遵循的契约)

    • 不相同 :

      1. 接口 : 只能描述结构,不能有任何实现代码,一个类可以实现多个接口

      2. 抽象类 : 既可以包含抽象方法,也可以包含具体方法,一个类只能继承一个抽象类

    • 一个类可以实现多个接口

      //FlyInterface接口
      interface FlyInterface {
          fly(): void;
      }
      ​
      //定义SwimInterface接口
      interface SwimInterface {
          swim(): void;
      }
      ​
      //Duck类实现了FlyInterface和SwimInterface两个接口
      class Duck implements FlyInterface,SwimInterface {
          fly():void {
              console.log('鸭子可以飞');
          }
          swim():void{
              console.log('鸭子可以游泳');
          }
      }
      ​
      //创建一个Duck实例
      const duck = new Duck();
      duck.fly();
      duck.swim();
八. 泛型

泛型允许我们在定义函数 \ 类或者接口时,使用类型参数来表示未指定的类型,这些参数在具体使用时,才被指定具体的类型,泛型能让同一段代码适用多种类型,同时仍然保持类型的安全性

举例 : 如下代码中<T>就是泛型,(不一定叫T),设置泛型后可在函数中使用T来表示该类型;

  • 泛型函数

    function logData<T>(data: T) {
        console.log(data);
    }
    ​
    ​
    logData<number>(100)
    logData<string>('hello')
  • 泛型可以有多个

    function logData<T, U>(data1: T, data2: U): T | U {
        // Date.now() % 2 ? console.log(data1) : console.log(data2);
        return Date.now() % 2 ? data1 : data2
    }
    ​
    logData<number, boolean>(100, true)
    logData<string, number>('hello', 666)
  • 泛型接口

    interface PersonInterface<T> {
        name: string,
        age: number,
        extraInfo: T
    }
    ​
    let p :PersonInterface<number> = {
        name:'tom',
        age:18,
        extraInfo:250
    }
  • 泛型约束

    interface PersonInterface {
        name: string,
        age: number
    }
    ​
    function logPerson<T extends PersonInterface>(info: T): void {
        console.log(`我叫${info.name},今年${info.age}岁了`);
    }
    ​
    logPerson({ name: '张三', age: 18 })
  • 泛型类

    class Person<T> {
        constructor(
            public name: string,
            public age: number,
            public extraInfo: T
        ) { }
        speak() {
            console.log(`我叫${this.name},今年${this.age}岁了`);
            console.log(this.extraInfo);
        }
    }
    ​
    //测试代码
    const p1 = new Person<number>("tom", 30, 250)
九. 类型声明文件

类型声明文件是TypeScript中的一种特殊文件,通常以.d.ts作为扩展名.它的主要作用是为现有的JavaScript代码提供类型信息,使得TypeScript能够在使用这些JavaScript库或模块时进行类型检查和提示

例如,我要在ts文件中引入demo.js文件中的两个函数 :

export function add(a, b) {
    return a + b
}
​
export function mul(a, b) {
    return a * b
}

那么我们需要创建.d.ts文件(demo.d.ts)

declare function add(a: number, b: number): number;
declare function mul(a: number, b: number): number;
​
export { add, mul };

那么便可在index.ts文件中引入这两个函数

import {add,mul} from './demo.js'
console.log(add(1,2));
console.log(mul(3,4));

同时注意 :

  1. 需要将配置文件tsconfig.json中的module设置为es6;

  2. 以及index.html文件中的body部分要改为以下内容

浏览器控制台结果如下:


http://www.kler.cn/a/536105.html

相关文章:

  • TCP长连接、HTTP短轮询、HTTP长轮询、HTTP长连接、WebSocket的区别
  • 凝思60重置密码
  • 点(线)集最小包围外轮廓效果赏析
  • 数据分析系列--[11] RapidMiner,K-Means聚类分析(含数据集)
  • MySQL的 MVCC详解
  • JS-对象-BOM
  • SMU winter 2025 Personal Round 2
  • RabbitMQ:windows最新版本4.0.5安装方案
  • Rust unresolved import `crate::xxx` 报错解决
  • C++11—可变参数模板
  • 【CS61A 2024秋】Python入门课,全过程记录P6(Week12 Interpreters开始,更新于2025/2/7)
  • 【HarmonyOS NEXT】设备显示白屏 syswarning happended in XXX
  • C++ 类和对象 详细版(上)
  • PT2021K 单触控同步输出 IC
  • Python爬虫-如何正确解决起点中文网的无限debugger
  • CF 278A.Circle Line
  • Python中__init__.py文件的作用
  • GitPuk快速安装配置教程(入门级)
  • c# Lazy<T>单例模式 - 延迟初始化单例实例示例与详解
  • Android GPS功耗优化思考点
  • 【Leetcode 热题 100】136. 只出现一次的数字
  • ControlNet 风格迁移介绍
  • 【R语言】apply函数族
  • mysql优化-升级8.0服务启动失败
  • 250207-MacOS修改Ollama模型下载及运行的路径
  • python编程-内置函数 bytes() , bytearray()详解