typescript学习文档(二)

1、安装typescript

全局安装:npm install -g typescript
检查是否安装成功(出现版本号表示安装成功):tsc -v
如果使用tsc指令出现如下错误:
image.png
解决办法:

  1. 以管理员的身份运行vscode
  2. 终端执行:get-ExecutionPolicy,结果:Restricted
  3. 终端执行:set-ExecutionPolicy RemoteSigned
  4. 终端执行:get-ExecutionPolicy,结果:RemoteSigned

image.png

2、编写第一个typescript程序

  1. 创建ts文件:hello_world.ts
  2. 编写代码:
(() => {
    function sayHi(str:String){
        return '你好:'+str
    }
    //sayHi(123) 报错: 类型错误
    sayHi('bob')
    let num:Number = 123
    console.log(num)
})()
  1. 编译ts代码:

**注意:**浏览器无法识别ts代码,故需要把ts代码编译成js代码后才能被浏览器解析使用,即 **ts --> js --> 执行**

  1. 手动编译:**tsc ./hello_world.ts**
  2. vscode自动编译:

image.png
编译ts后,会生成一个对应的js文件:
image.pngimage.png

3、基本数据类型声明

基本数据类型:number、string、boolean、undefined、null、symbol、bigint

//基本数据类型声明
//注意:声明类型时,既可以用大写又可以使用它小写,如number、Number都可以
//ts编译器有自动检测类型机制,即声明变量但没有声明类型时,ts编译器会根据语法自动检测该变量的类型,故后续为改变量赋值为其他类型时会报错
//数字类型:number
let num:number = 123
//num = 'abc'//报错
num = 456
console.log(num)

//字符串类型:string
let str:string = 'you'
let str1 = 'me'
// str = 123 报错
str = 'he'
//str1 = 123 报错   自动检测类型机制
console.log(str,str1)

// 布尔类型:boolean
let flag:boolean = true
// flag = 'true' 报错
flag = false
console.log(flag)

// undefined和null:不常用
let u:undefined = undefined
let n:null = null
// u = 123 报错
// n = {} 报错
console.log(u,n)

4、引用数据类型声明(object、array)

// 引用数据类型声明
//数组:Array
//第一种方法:
let arr1:[] = [] //定义空数组,没有指定元素类型
// arr1 = [1,2,3] 报错
// arr1 = ['a','v'] 报错
arr1 = []

let arr2:number[] = [] //指定数组中的元素必须是number类型
// arr2 = ['a','v'] //报错
arr2 = []
arr2 = [1,2,3] 

//第二种方法:泛型<T>
let arr3:Array<number> = [] //声明数组,并指定元素的类型为number类型
// arr3 = ['a','v'] 报错
arr3 = []
arr3 = [1,2,3]

let arr4:Array<string> = ['a','b'] //声明数组,并指定元素的类型为string类型
arr4 = []
arr4 = ['this','is','a','apple']

//Object: 表示非原始类型,除了number、string、boolean之外的类型
let obj:object = {}
// obj = 123 报错
// obj = 'abc' 报错
// obj = true 报错
// obj = null 报错 与版本有关,有的版本不会报错,以及在严格模式下也会报错
// obj = undefined 报错 与版本有关,有的版本不会报错,以及在严格模式下也会报错
obj = [] //不会报错,数组也属于对象,typeof [] === 'object' ----> true
obj = new String() //不会报错,得到的是一个实例化“对象”
obj = String //不会报错,是一个类

5、声明any和void类型

//any和void类型声明:
//any:表示任何类型
let test:any = 123
test = 'abc'
test = true
test = false
test = {}
test = []

let newArr:any[] = [100,1,3,'this',false,true]
// console.log(newArr[0].split('')) 
//报错:newArr[0].split is not a function  
//原因:newArr[0]是100,这是一个数字类型,数字类型没有split方法
// any的优点:在我们不知道返回值类型的时候,使用any声明类型,就能避免报错
// any的缺点:在我们需要操作变量时,由于无法确定值类型,所以可能出现错误的情况

//void:表示没有类型,一般用于声明函数没有返回值,声明变量没有意义
//声明函数:
function demo():void{
    console.log(123)
}
console.log(demo()) //undefined

//声明变量:
let u1:void = undefined
// let u2:void = null 报错
// let u3:void = 12 报错

6、类型推论

类型推论:如果没有明确的指定类型,那么 TypeScript 会依照类型推论(Type Inference)的规则推断出一个类型

let test1 = 123 //ts会依照类型推论的规则推断出test1是一个number类型
// test1 = 'abc' 报错

let test2  //ts会依照类型推论的规则推断出test2是一个any类型
test2 = 123
test2 = 'this'
test2 = false

7、联合类型

**联合类型:**表示取值可以为多种类型中的一种
注意:

  • 联合类型的变量在被赋值的时候,会根据类型推论的规则推断出一个类型
  • 只能访问此联合类型的所有类型里共有的属性或方法
let test3: number | boolean = true //变量test3的值只能是number或者boolean
test3 = 1 
test3 = false
// test3 = 'test' 报错

let test4:number | boolean | string = false
test4 = 123
// console.log(test4.split('')) 报错,原因:当前的变量test4是一个number类型,没有split方法
test4 = 'this is a apple'
console.log(test4.split(''))
test4 = false
// console.log(test4.split('')) 报错,原因:当前的变量test4是一个boolean类型,没有split方法
console.log(test4.toString()) //toString(),是number、string、boolean类型共有的方法

8、接口

对象类型

**接口:**可以理解为是一种约束,它是对行为的抽象,而具体如何行动需要由类(classes)去实现(implement)
TypeScript 中的接口是一个非常灵活的概念,除了可用于对类的一部分行为进行抽象以外,也常用于对「对象的形状(Shape)」进行描述。
注意:

  1. 接口一般首字母大写,有的编程语言中会建议接口的名称加上 I 前缀
  2. 定义的变量比接口少一些属性和多一些属性都是不允许的(赋值的时候,变量的形状必须和接口的形状保持一致)
interface IPerson {
    name:string;
    age:number;
    height:string;
}

let tom:IPerson = {
    name:'Tom',
    age:18,
    height:'180cm',
    // sex:'男'报错
}

可选属性:

通过 ? 来定义,可选属性可以被定义也可以不被定义

interface IStudent {
    name:string;
    age:number;
    sex?:string; //表示sex是一个可选属性
}
let bob:IStudent = {
    name:'bob',
    age:23,
    sex:'男',
    // width:'100cm' 报错,不可以添加属性
}
let john:IStudent = {
    name:'john',
    age:20
}

任意属性:

注意:

  1. 一旦定义了任意属性,那么确定属性和可选属性的类型都必须是它的类型的子集
  2. 一个接口中只能定义一个任意属性
interface IPeople {
    name:string;
    count?:number;
    [propName:string]:any; //定义任意类型
}
let one:IPeople = {
    name:'团体',
    address:'china',
    phone:'10086'
}
console.log(one)
//注意:
//一旦定义了任意属性,那么确定属性和可选属性的类型都必须是它的类型的子集
//一个接口中只能定义一个任意属性
interface Person {
    name: string;
    age: number;
    [propName: string]: string | number;
}

let wick: Person = {
    name: 'wick',
    age: 25,
    gender: 'male',
};

可读属性:

使用readonly定义
**注意:**只读的约束存在于第一次给对象赋值的时候,而不是第一次给只读属性赋值的时候

interface IPerson1 {
    readonly id:number;
    name:string;
}
let wenston:IPerson1 = {
    id:10,
    name:'wenston'
}
// wenston.id = 1235 报错,只读属性不允许更改

数组类型

interface INewArray {
    [index:number]:number
}
let newArr1:INewArray = [1,2,3]
// let newArr2:INewArray = [1,2,'abc'] 报错

函数类型

interface ISearchFun {
//  (变量名:类型,...):返回值类型
    (a:string,b:string):boolean
}
const fun1:ISearchFun = function(a:string,b:string):boolean{
    return a.search(b) !== -1
}

9、函数定义

函数声明:

function sum(a:number,b:number):number{
    return a + b
}

函数表达式:

let sum1 = function(a:number,b:number):number{
    return a+b
}

函数完整写法:

let sum2:(a:number,b:number)=>number = function(a:number,b:number):number{
    return a+b
}

可选参数:

通过 ?定义
**注意:**可选参数不能放在必选参数之前,如:y不能放在x之前

let outStr = function(x:string,y?:string):string{
    return x + y
}
console.log(outStr('你好'))
console.log(outStr('你好','龙傲天'))
// 报错:变量z会报错,原因:可选参数不能放在必选参数之前
// let outStr1 = function(x:string,y?:string,z:string):string{ 
//     return x + y
// }

默认参数:

**注意:**默认参数可以放在可选和必选参数之后

let outStr2 = function(x:string,y?:string,z:string = '张三'):string{
    return x + y + z
}
console.log(outStr2('你好'))
let outStr3 = function(x:string,z:string = '李四',y?:string):string{
    return x + y + z
}
console.log(outStr3('你好'))

剩余参数:

通过 …rest 实现

let outInfo = function(a:string,b:string,...args:string[]){
    console.log(a,b,args)
}
// outInfo('a','b',1,2,3,4)//报错
outInfo('a','b','this','is','my','wife')

函数重载:

重载允许一个函数接受不同数量或类型的参数时,作出不同的处理

//实现一个需求:当参数x、y都是数字类型时进行相加,当x、y都是字符串类型时进行字符串拼接
//函数实现:
//然而这样有一个缺点,就是不能够精确的表达,输入为数字的时候,输出也应该为数字,输入为字符串的时候,输出也应该为字符串
function concatFun1(x:string | number,y:string | number):string | number{
    if(typeof x === 'string' && typeof y === 'string'){
        return x + y //字符串拼接
    }else if(typeof x === 'number' && typeof y === 'number'){
        return x + y //求和
    }else{
        return '情况不存在'
    }
}
//将鼠标放在下面两个函数调用上,代码提示不够准确,代码提示:
//function concatFun1(x:string | number,y:string | number):string | number
console.log(concatFun1(1,2))
console.log(concatFun1('this','is'))
//函数重载实现:
function concatFun2(x:string,y:string):string;
function concatFun2(x:number,y:number):number;
function concatFun2(x:string | number,y:string | number):string | number{
    if(typeof x === 'string' && typeof y === 'string'){
        return x + y //字符串拼接
    }else if(typeof x === 'number' && typeof y === 'number'){
        return x + y //求和
    }else{
        return '情况不存在'
    }
}
//在编辑器的代码提示中,可以正确的看到前两个提示
console.log(concatFun2(1,2)) // 代码提示:function concatFun2(x:number,y:number):number
console.log(concatFun2('this','is'))// 代码提示:function concatFun2(x:string,y:string):string

10、类型断言

**定义:**手动指定一个值的类型
语法:

  1. 值 as 类型
  2. <类型>值

**注意:**类型断言并不是将函数强制转换为对应的类型

// function getLength(x:string | number):number{
//     if(x.length){ //报错,因为length属性只有string类型值才拥有,但变量x不能确定是string还是number
//         return x.length //报错,同上
//     }else{
//         return x.toString().length
//     }
// }
//使用类型断言解决:
function getLength(x:string | number):number{
    if((x as string).length){
        console.log('1')
        return (<string>x).length
    }else{
        console.log('2')
        return x.toString().length
    }
}
console.log(getLength('123'))
console.log(getLength(123))

注意:

  1. 将任何一个类型断言为 any,any类型是可以访问任何属性和方法的
  2. 它极有可能掩盖了真正的类型错误,所以如果不是非常确定,就不要使用 as any
  3. 一方面不能滥用 as any,另一方面也不要完全否定它的作用,我们需要在类型的严格性和开发的便利性之间掌握平衡

将任何一个类型断言为any:

// window.a = 10 报错,我们需要在window上添加一个a属性,但typescript提示window中没有a属性
//将window断言为any类型,any类型可以访问任何属性和方法
(window as any).a = 10

将any断言为一个具体类型:

//将any 断言为一个具体类型
function sumTestFun(x:any,y:any):any{
    return x + y
}
//传递的参数都为数值类型,所以可以断言返回的值是数值类型
let a12 = sumTestFun(1,2) as number // a --> 数值类型
//传递的参数都为字符串类型,所以可以断言返回的值是字符串类型
let b12 = sumTestFun('1','2') as string // b --> 字符串类型

11、类型别名

使用 type 定义,一般用于定义联合类型

type s = string //s 代表了 string
//如下:a和b都是string类型
let a:string = 'this'
let b:s = 'is'

type snb = string | number | boolean
//test和test1 都是 string、number、boolean中的一种类型
let test: string | number | boolean = 'this is'
let test1:snb = 'my wife'

12、字符串字面量类型

使用 type 定义,约束取值只能是某几个字符串中的一个

type name = '张三' | '张伟' | '张益达'
let babyName:name = '张伟' //只能取 name中的其中一个
// let babyName1:name = '张根硕'  报错

13、元组(Tuple)

数组合并了相同类型的对象,而元组(Tuple)合并了不同类型的对象

let arr:number[] = [1,2,3,4] //这里指定了数组中的元素只能是number类型
//如果想数组中的元素可以有多种类型,可以使用元组定义:
//通过元组指定了第一个元素的类型为number,第二个元素的类型为string,必须保持一致
let Tarr:[number,string] = [123,'true']
//添加元素的时候,需要是元组中指定的类型,如上便是number或string类型
Tarr.push('456')
Tarr.push(456)
// Tarr.push(true) 报错,原因:添加的元素是boolean类型

//可以只赋值其中一项
let Ttom:[string,number];
// Ttom[0] = 123  报错
Ttom[0] = 'abc' 

//当直接对元组类型的变量进行初始化或赋值时,需要提供所有元组类型中指定的项
let Twick:[string,number]
// Twick = ['123'] 报错
Twick = ['123',123]

14、枚举(Enum)

  1. 枚举(Enum)类型用于取值被限定在一定范围内的场景,比如一周只能有七天,颜色限定为红绿蓝等
  2. 枚举成员会被赋值为从 0 开始递增的数字,同时也会对枚举值到枚举名进行反向映射(枚举名和枚举值作为键值对,可以相互获取)即:枚举[枚举名] — 枚举[枚举值]
  3. 未手动赋值的枚举项会接着上一个枚举项递增

普通枚举:

enum Days {
    one,
    two,
    three
}
console.log(Days.one)
console.log(Days)//{ '0': 'one', '1': 'two', '2': 'three', one: 0, two: 1, three: 2 }

手动赋值:

enum Days1 {
    one = 7,
    two,
    three,
    four
}
//由下面的代码可得:未手动赋值的枚举项会接着上一个枚举项递增
console.log(Days1.one)// 7
console.log(Days1.two)// 8
console.log(Days1.three)// 9

//如果出现相同枚举值,后面的枚举值会覆盖前面的枚举值
enum Days2 {
    one,
    two,
    three,
    four=1,
}
console.log(Days2)//{'0': 'one','1': 'four','2': 'three',one: 0,two: 1,three: 2,four: 1}

常数项和计算所得项:

//其中red为常数项,blue为计算所得项
enum color {
    red,
    blue='blue'.length,
}
//如果紧接在计算所得项后面的是未手动赋值的项,那么它就会因为无法获得初始值而报错,如下:
enum color1{
    red,
    blue='blue'.length,
    // green, 报错
    green=1
}

常数枚举:

使用const enum定义
常数枚举与普通枚举的区别: 常数枚举会在编译阶段被删除,并且不能包含计算成员

const enum Direction{
    up,
    down=9,
    left,
    right,
    // to='to'.length 报错
}
console.log(Direction.up) // 0 /* Direction.up */
console.log(Direction.down)// 9 /* Direction.down */
console.log(Direction.left)// 10 /* Direction.left */

外部枚举:

使用 declare enum 定义,常用于声明文件中
外部枚举与普通枚举的区别: 外部枚举会用于编译时的检查,编译结果中会被删除,并且不能包含计算成员

declare enum Direction1{
    up,
    down,
    left,
    right=9,
    // to='to'.length 报错
}
// console.log(Direction1.up)
// console.log(Direction1.right)


//同时使用 declare 和 const 也是可以的:
declare const enum Direction2{
    up,
    down,
    left,
    right
}
console.log(Direction2.down)// 1 /* Direction2.down */
console.log(Direction2.right)// 3 /* Direction2.right */

15、类

属性和方法的定义:

class Person{
    name:string
    age:number
    constructor(name:string,age:number){
        this.name = name
        this.age = age
    }
    sayHi(str:string):void{
        console.log('hi ',str)
    }
}
let p = new Person('张三',18) //new会执行constructor构造函数
p.sayHi('李四')

类的继承:

  1. 通过extends实现继承功能
  2. 子类中通过super()调用父类的构造函数constructor
  3. 子类中可以使用父类的属性和方法,通过super调用父类的方法,this调用父类的属性
  4. 字类可以重写父类的方法
//父类:
class Animal {
    name:string
    age:number
    constructor(name:string,age:number){
        this.name = name
        this.age = age
    }
    outInfo(){
        console.log('我是一只动物')
    }
    sayHi(str:string){
        console.log('Hi',str)
    }
}
//子类:
class Dog extends Animal{
    color:string
    constructor(name:string,age:number,color:string){
        //通过super调用父类Animal的构造函数
        super(name,age)
        this.color = color
    }
    //重写父类Animal的sayHi方法
    sayHi(str:string):void{
        //通过super调用父类Animal的方法
        super.outInfo()
        console.log(`${str},我的名字是${this.name},我已经${this.age}岁了,我的颜色是${this.color}`)
    }
}
let dog = new Dog('拉布拉多',3,'白色')
dog.sayHi('大家好')

类的存取器:

class Name{
    firstName:string
    lastName:string
    constructor(firstName:string,lastName:string){
        this.firstName = firstName
        this.lastName = lastName
    }
    //设置存取器:
    //读取器
    get fullName(){
        return this.firstName + ' - ' + this.lastName
    }
    //设置器
    set fullName(value){
        let names = value.split('-')
        this.firstName = names[0]
        this.lastName = names[1]
    }
}
let zs = new Name('张','三')
console.log(zs.fullName)
zs.fullName = 'john-wick'
console.log(zs.fullName)

类的静态成员:

  1. 通过static定义
  2. 静态属性和方法只属于类自己,即可以通过类调用,不能通过实例化对象调用
  3. 在类中,不能使用this调用静态属性和方法
class Cat{
    static color:string = '红色'
    constructor(){
        // this.color = '白色' //color是静态属性,不能通过this操作
    }
    sayHi(str:string){
        console.log(str)
    }
    static outInfo(str:string):string{
        return `我是一只猫,${str}`
    }
}
let cat = new Cat()
console.log(Cat.color) //红色
// console.log(cat.color) //报错,实例化对象cat上不存在color属性
console.log(Cat.outInfo('yes'))
cat.sayHi('你好')
// cat.outInfo('yes')//报错,实例化对象cat上不存在outInfo方法

类的访问修饰符:

  1. public:修饰的属性或方法是公有的,可以在任何地方被访问到,默认所有的属性和方法都是 public 的
  2. private:修饰的属性或方法是私有的,不能在声明它的类的外部访问,包括子类中也不可以被访问
  3. protected:修饰的属性或方法是受保护的,它和 private 类似,区别是它在子类中是允许被访问的

总结:

  1. public:没有限制,在任何地方都可以被访问到
  2. private:只能在当前类中访问,其他任何地方都不能被访问到
  3. protected:只能在当前类和子类中访问,其他任何地方都不能被访问到
class Man{
    public name:string
    private age:number
    protected gender:string
    constructor(name:string,age:number,gender:string){
        this.name = name
        this.age = age
        this.gender = gender
    }
    public sayName(){
        console.log('my name is '+this.name)
    }
    private sayAge(){
        console.log('my age is '+this.age)
    }
    protected sayGender(){
        console.log('my gender is '+this.gender)
    }
}
class M extends Man{
    constructor(){
        super('张三',18,'男')
    }
    outInfo(){
        console.log(`姓名:${this.name}`)
        // console.log(`年龄:${this.age}`)//报错,age是私有属性(private),只能在Man类中使用
        console.log(`性别:${this.gender}`)// gender是保护属性(protected),只能在Man类和其子类M中使用
        super.sayName()
        // super.sayAge() 报错
        super.sayGender()
    }
}
let man = new Man('李四',23,'男')
console.log(man.name)
// console.log(man.age)//不能访问
// console.log(man.gender)//不能访问

let m = new M()
console.log(m.name)
m.outInfo() 
m.sayName()

参数属性:

修饰符(public、private、protected)和readonly可以用在构造函数参数中
作用:等同于在类中定义该属性,同时为该属性赋值,使代码更简单
readonly:关键字,表示只读,只允许出现在属性声明或索引签名或构造函数中
注意:

  1. 只读属性只能在构造函数中修改
  2. readonly和其他修饰符同时使用时,readonly需要放在后面,如:public readonly
class Test{
    readonly name:string
    constructor(name:string){
        this.name = name
    }
}
let t = new Test('李四')
console.log(t.name)


class Test1{
    //readonly age:number  会在类中创建一个age:number属性,并且会在构造函数中为age赋值,即:this.age = age
    constructor(readonly age:number){
    }
    //等同于:
    // readonly age:number
    // constructor(age:number){
    //     this.age = age
    // }
}
let t1 = new Test1(18)
console.log(t1.age)


class Test2{
    constructor(public age:number){


    }
}
let t2 = new Test2(233)
console.log(t2.age)


class Test3{
    constructor(private age:number){


    }
    //等同于:
    // private age:number
    // constructor(age:number){
    //     this.age = age
    // }
}
let t3 = new Test3(54)
// console.log(t3.age) 报错,无法访问age,因为这里的age是一个私有属性

抽象类:

  1. 使用 abstract 定义抽象类和其中的方法、属性
  2. 抽象类是不允许被实例化
  3. 抽象类中的抽象属性和抽象方法,在子类中必须被实现
  4. 抽象类中抽象属性不能被初始化值,也不能在构造函数中被访问
  5. 抽象类中的抽象方法只能定义,但不能被实现
abstract class Test4{
    abstract color:string 
    // constructor(color:string){
    //     //不能在构造函数中访问类的抽象属性
    //     // this.color = color 报错
    // }
    abstract sayHi()//抽象类中的抽象方法不能被实现,只能被子类实现
}


class Test5 extends Test4{
    color:string
    age:number
    constructor(color:string,age:number){
        super()
        this.color = color
        this.age = age
    }
    sayHi() {
        console.log('hi')
    }
    outInfo(){
        console.log('输出一些信息')
    }
}
let test5 = new Test5('白色',18)
console.log(test5.color)
console.log(test5.color)
test5.sayHi()
test5.outInfo()

类的类型:

与接口类似

class Car{
    color:string
    constructor(color:string){
        this.color = color
    }
}
class Bmw extends Car{
    constructor(color:string){
        super(color)
    }
}
//因为Bmw继承于Car,所以可以使用Car指定bmw的类型,因为Bmw没有在自身类中添加其他属性和方法,
//故Bmw与Car是保持一致的,所以可以使用Bmw指定car_1的类型
let car_1:Bmw = new Car('白色')
let bmw:Car = new Bmw('黑色')


class Benz extends Car{
    name:string
    constructor(color:string,name:string){
        super(color)
        this.name = name
    }
}
//因为Benz是继承于Car,所以可以使用Car指定benz的类型,因为Benz自身上有一些其他属性和方法,
//故Benz和Car并不完全一致,所以不能使用Benz指定car_2的类型
let benz:Car = new Benz('红色','C300L')
// let car_2:Benz = new Car('绿色') 报错

16、类与接口

有时候不同类之间可以有一些共有的特性,这时候就可以把特性提取成接口(interfaces)
用 implements 关键字来实现

接口定义与实现:

interface ISing{
    name:string
    sing()
}
interface IDance{
    dance()
}
interface IRap{
    rap()
}
interface IBaseketball{
    playBaseketball()
}
//一个类实现了一个或多个接口,那么所有接口中的函数和属性都需要被实现,否则会报错
//接口定义中,只需要定义,不需要实现
class Ji implements ISing,IDance,IRap,IBaseketball{
    name: string;
    constructor(name:string){
        this.name = name
    }
    sing() {
        console.log('唱')
    }
    dance() {
        console.log('跳')
    }
    rap() {
        console.log('rap')
    }
    playBaseketball() {
        console.log('篮球')
    }
    sayName(){
        console.log('练习时长两年半的偶像练习生')
    }
}


class Cxk implements ISing,IDance,IRap,IBaseketball{
    name: string;
    constructor(name:string){
        this.name = name
    }
    sing() {
        console.log('唱')
    }
    dance() {
        console.log('跳')
    }
    rap() {
        console.log('rap')
    }
    playBaseketball() {
        console.log('篮球')
    }
    sayName(){
        console.log('蔡徐坤')
    }
}

接口继承接口:

  1. 接口之间可以继承,而且可以多继承
  2. 可以避免类需要实现多个接口,如果使用一个接口继承其他的多个接口,那么类只需要继承这一个接口便可以了,效果与类中实现多个接口一致
interface IColor{
    outColor(name:string)
}
interface IHeight{
    outHeight()
}
interface IWidth extends IColor,IHeight{
    outWidth()
}
class Monkey implements IWidth{
    height:string
    width:string
    constructor(height:string,width:string){
        this.height = height
        this.width = width
    }
    outColor(name:string):string {
        return name
    }
    outHeight() {
        return this.height
    }
    outWidth() {
        return this.width
    }
}
let monkey = new Monkey('170cm','23cm')
console.log(monkey.outColor('黄色'))
console.log(monkey.outHeight())
console.log(monkey.outWidth())

接口继承类:

  1. 接口只能继承类的属性和方法(实例属性和实例方法)
  2. 构造函数、静态属性、静态方法都是不会继承的
class NewPerson {
    name:string
    constructor(name:string){
        this.name = name
    }
    sayHi(){
        console.log('hi')
    }
}

interface IPersonTest extends NewPerson{
    address:string
    run()
}

let person:IPersonTest = {
    name:'张三',
    address:'中国',
    sayHi(){
        console.log('hello')
    },
    run() {
        console.log('跑步')
    },
}

17、声明合并

如果定义了两个相同名字的函数、接口或类,那么它们会合并成一个类型

函数合并:

可以使用函数重载定义多个函数类型

function concatFun2(x:string,y:string):string;
function concatFun2(x:number,y:number):number;
function concatFun2(x:string | number,y:string | number):string | number{
    if(typeof x === 'string' && typeof y === 'string'){
        return x + y //字符串拼接
    }else if(typeof x === 'number' && typeof y === 'number'){
        return x + y //求和
    }else{
        return '情况不存在'
    }
}
//在编辑器的代码提示中,可以正确的看到前两个提示
console.log(concatFun2(1,2)) // 代码提示:function concatFun2(x:number,y:number):number
console.log(concatFun2('this','is'))// 代码提示:function concatFun2(x:string,y:string):string

接口合并:

interface IA{
    price:number
}
interface IA{
    count:number
}
//等同于:
// interface IA{
//     price:number
//     count:number
// }
let aTest:IA = {
    price:100,
    count:99
}


interface IB{
    price:number,
}
interface IB{
    // price:string 接口合并时,合并的属性的类型必须是相同的,所以这里的price应该是number类型
    price:number
    count:number
}
let bTest:IB = {
    price:12,
    count:12
}


interface IC{
    name:'小白'
    age:number
}
interface IC{
    // name:'小红' 接口合并时,合并的属性的类型和字面量(值)必须是相同的,所以这里的name应该是’小白‘,而不是’小红‘
    name:'小白'
    age:number
}
let cTest:IC = {
    name:'小白',
    age:18
}


interface ID{
    width:string
    getColor(color:string)
}
interface ID{
    height:string
    getColor(color:string,temp:number)
}
//等同于:
interface ID{
    height:string
    width:string
    getColor(color:string)
    getColor(color:string,temp:number)
}

类合并:

类的合并与接口的合并规则一致

18、泛型

泛型(Generics)是指在定义函数、接口或类的时候,不预先指定具体的类型,而在使用的时候再指定类型的一种特性。

function createArr(length:number,value:any):any[]{
    let result = []
    for(let i=0;i<length;i++){
        result[i] = value
    }
    return result
}
console.log(createArr(10,'this'))
//上面这段代码进行编译不会报错,但是有一个缺陷,
//返回值是一个数组,但是里面元素的类型可以是任意类型,
//预期是返回数组中的元素类型与value类型一致
//使用泛型实现:
//T : 表示任意类型;如下代码可见,返回数组中的元素类型与value的类型始终保持一致
function createNewArr<T>(length:number,value:T):Array<T>{
    let result = []
    for(let i = 0;i< length;i++){
        result[i] = value
    }
    return result
}
//调用时可以指定它的具体类型为string或其他,也可以不手动指定,而让类型推论自动推算出来
console.log(createNewArr<string>(10,'this'))//value和返回的数组元素的类型都为string
console.log(createNewArr(5,0))//value和返回的数组元素的类型都为number(类型推论)

多个类型参数:

定义泛型时,可以定义多个类型参数

function swap<T,U>(tuple:[T,U]):[U,T]{
    return [tuple[1],tuple[0]]
}
console.log(swap([true,'this']))

泛型约束:

//在函数内部使用泛型变量时,由于事先不知道泛型的具体类型,不能随意操作它的属性和方法
function outVal<T>(arg:T):T{
    // console.log(arg.length) 报错,不知道泛型的具体类型,所以无法确定arg是否具有length属性
    return arg
}
//使用泛型约束,只允许这个函数传入那些包含length属性的变量:
interface ILengthWise{
    length:number
}
//由于 T 继承了 ILengthWise接口 ,所以传递的参数arg中必须包含(实现)接口中的length属性,
//如果没有,在编译阶段便会报错
function outNewVal<T extends ILengthWise>(arg:T):T{
    console.log(arg.length)
    return arg
}
console.log(outNewVal('this is'))
//多个类型参数之间也可以相互约束
//这样U中就不会出现T中不存在的字段
function copyFields<T extends U, U>(target: T, source: U): T {
    for (let id in source) {
        target[id] = (<T>source)[id];
    }
    return target;
}
let x = { a: 1, b: 2, c: 3, d: 4 };
copyFields(x, { b: 10, d: 20 });
// copyFields(x, { b: 5, d: 9, e:8}); //报错,第二个参数中的属性,必须在x中存在

泛型接口:

使用一个包含泛型的接口,来定义函数的形状

interface ICreateArr{
    <T>(length:number,value:T):Array<T>
}
let newCreateArr:ICreateArr = function <T>(length:number,value:T):Array<T>{
    let result:T[] = []
    for(let i=0;i<length;i++){
        result[i] = value
    }
    return result
}
console.log(newCreateArr(5,true))
console.log(newCreateArr<boolean>(5,false))
//将泛型参数提前到接口名上:
//注意:如将泛型参数放到接口名后定义,那么在使用接口的时候就需要定义泛型的类型
interface ICreateNewArr<T>{
    (length:number,value:T):Array<T>
}
let newCreateNewArr:ICreateNewArr<string> = function <T>(length:number,value:T):T[]{
    let result:T[] = []
    for(let i=0;i<length;i++){
        result[i] = value
    }
    return result
}
//报错,因为在使用接口ICreateNewArr时,已经定义了泛型的类型为string,
//所以这里只能传递string类型的参数
// console.log(newCreateNewArr(5,false)) 
console.log(newCreateNewArr(5,'are'))

泛型类:

与泛型接口类似

class CreatTest<T>{
    value:T
    add:(x:T,y:T) => T
}
let ct = new CreatTest<number>()
// ct.value = 'this' 报错,'this'是string类型,而value需要的是number类型
ct.value = 1
//报错,x、y需要的是number类型,返回值的类型也应该是number类型
// ct.add = function(x:string,y:string){ return x + y }
//报错,返回值需要与 T 保持一致,即返回值需要时number
// ct.add = function(x:number,y:number){return ''}
ct.add = function(x:number,y:number){return x+y}

泛型参数的默认类型:

当使用泛型时没有在代码中直接指定类型参数,从实际值参数中也无法推测出时,这个默认类型就会起作用

function createArray<T = string>(length:number,value:T):T[]{
    let result:T[] = []
    for(let i=0;i<length;i++){
        result[i] = value
    }
    return result
}
  • 16
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
1. Two Sum 2. Add Two Numbers 3. Longest Substring Without Repeating Characters 4. Median of Two Sorted Arrays 5. Longest Palindromic Substring 6. ZigZag Conversion 7. Reverse Integer 8. String to Integer (atoi) 9. Palindrome Number 10. Regular Expression Matching 11. Container With Most Water 12. Integer to Roman 13. Roman to Integer 14. Longest Common Prefix 15. 3Sum 16. 3Sum Closest 17. Letter Combinations of a Phone Number 18. 4Sum 19. Remove Nth Node From End of List 20. Valid Parentheses 21. Merge Two Sorted Lists 22. Generate Parentheses 23. Swap Nodes in Pairs 24. Reverse Nodes in k-Group 25. Remove Duplicates from Sorted Array 26. Remove Element 27. Implement strStr() 28. Divide Two Integers 29. Substring with Concatenation of All Words 30. Next Permutation 31. Longest Valid Parentheses 32. Search in Rotated Sorted Array 33. Search for a Range 34. Find First and Last Position of Element in Sorted Array 35. Valid Sudoku 36. Sudoku Solver 37. Count and Say 38. Combination Sum 39. Combination Sum II 40. First Missing Positive 41. Trapping Rain Water 42. Jump Game 43. Merge Intervals 44. Insert Interval 45. Unique Paths 46. Minimum Path Sum 47. Climbing Stairs 48. Permutations 49. Permutations II 50. Rotate Image 51. Group Anagrams 52. Pow(x, n) 53. Maximum Subarray 54. Spiral Matrix 55. Jump Game II 56. Merge k Sorted Lists 57. Insertion Sort List 58. Sort List 59. Largest Rectangle in Histogram 60. Valid Number 61. Word Search 62. Minimum Window Substring 63. Unique Binary Search Trees 64. Unique Binary Search Trees II 65. Interleaving String 66. Maximum Product Subarray 67. Binary Tree Inorder Traversal 68. Binary Tree Preorder Traversal 69. Binary Tree Postorder Traversal 70. Flatten Binary Tree to Linked List 71. Construct Binary Tree from Preorder and Inorder Traversal 72. Construct Binary Tree from Inorder and Postorder Traversal 73. Binary Tree Level Order Traversal 74. Binary Tree Zigzag Level Order Traversal 75. Convert Sorted Array to Binary Search Tree 76. Convert Sorted List to Binary Search Tree 77. Recover Binary Search Tree 78. Sum Root to Leaf Numbers 79. Path Sum 80. Path Sum II 81. Binary Tree Maximum Path Sum 82. Populating Next Right Pointers in Each Node 83. Populating Next Right Pointers in Each Node II 84. Reverse Linked List 85. Reverse Linked List II 86. Partition List 87. Rotate List 88. Remove Duplicates from Sorted List 89. Remove Duplicates from Sorted List II 90. Intersection of Two Linked Lists 91. Linked List Cycle 92. Linked List Cycle II 93. Reorder List 94. Binary Tree Upside Down 95. Binary Tree Right Side View 96. Palindrome Linked List 97. Convert Binary Search Tree to Sorted Doubly Linked List 98. Lowest Common Ancestor of a Binary Tree 99. Lowest Common Ancestor of a Binary Search Tree 100. Binary Tree Level Order Traversal II
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值