Spiga

前端提升3:TS

2025-03-15 10:19:14

TypeScript 是一种由微软开发的自由开源的编程语言,他是JavaScript的一个超集,扩展了JavaScript的 语法,主要提供了类型系统和对 ES6 的支持。

一、类型定义

window.onload=function(){
    //类型系统
    let flag:boolean = false;  //布尔类型
    let num:number = 15;  //数值类型
    let str:string = 'abc'; //字符串类型
    let str2:string=`hello,${str}`;
    let msg:string = `hello,${str},${num}`;
    let u: undefined = undefined;
    let n: null = null;

    //为什么要TS
    // function sum(n1:number,n2:number){
    //     return n1+n2
    // };
    // sum(10,20);
    // sum('10',20);

    let count:number = 10;
    count = 100;

    //声明的变量有多种类型——联合类型
    let id:number | string = 10;
    id = '111';

    //任意类型 :any   万能类型
    let x:any = true;
    x = 111;
    x = 'abc';


    //引用类型
    //数组
    let arr:number[] = [1,2,3,4,5];
    //最简单的方法是使用「类型 + 方括号」来表示数组: number[]
    let arr2:string[] = ['a','b','hello'];
    let arr3:(number|string)[] = ['a',100,'hello'];
    let arr4:any[] = ['a',100,'hello'];
    //数组泛型 Array<类型>
    let arr5:Array<number>= [1,2,3,4,5];
    let arr6:Array<string>= ['a','b','hello'];
    let arr7:Array<number|string> = ['a',100,'hello'];
    let arr8:Array<any> = ['a',100,'hello'];


    //对象
    //接口 interface   合同
    // 在面向对象语言中,接口(Interfaces)是一个很重要的概念,它是对行为的抽象
    // 接口(Interfaces)可以用于对「对象的形状(Shape)」进行描述。  
    interface IPerson {  //定义接口 首字母大写,在项目实战中,习惯在接口前添加I 
        //变量的声明
        //方法的声明
        id:number;  //确定属性(必填)
        name:string; //确定属性(必填)
    }
    let obj: IPerson = {  //受到接口的约束
        id:1,
        name:'John'
    }

    let tom :IPerson ={
        id:10,
        name:'tom'
    }


    //扩展
    let items:Array<IPerson> = [{id:1,name:'a'},{id:2,name:'b'},{id:3,name:'c'}];
    let items2:IPerson[] = [{id:1,name:'a'},{id:2,name:'b'},{id:3,name:'c'}];
    let items3:Array<{id:number,name:string}> = [{id:1,name:'a'},{id:2,name:'b'},{id:3,name:'c'}];


    //函数  输入类型(参数类型)和输出类型(返回值)
    function sum2(n1:number,n2:number):number{
        return n1+n2
    };
    sum2(10,20);
    let sum3 = (n1:number,n2:number):number=>n1+n2;
    sum3(100,20);
    //无返回值
    function sum4(n1:number,n2:number):void{
        if(n1>n2){
            console.log('n1>n2')
        }
    };
    sum4(20,10);
    //默认值
    let sum5 = (n1:number=1,n2:number=1):number=>n1+n2;
    sum5(100,20);
    sum5();

    //选填参数
    let sum6 = (n1:number=1,n2?:number):number=>n2?n1+n2:n1;
    sum6(100);

    //常见的写法
    const fn1 =(x,y)=>[x,y];
    //添加类型
    const fn2 = (x:string,y:string):Array<string>=>[x,y]
    fn2('hello','abc')

    //定义对象
    interface IPerson2{
        id:number;
        name:string;
        age:number;
    }
    interface IOther {
        pid:number;
        title:string;
    }
    const o1:IPerson2 = {id:1,name:'Lily',age:18};
    
    const fn3 = ({id,name}:IPerson2):IOther=>({pid:id,title:name})
    fn3(o1);

    //过滤
    //[1,2,3,4,5] 过滤
    const items4:Array<number> = [1,2,3,4,5];
    const filter:number[] = items4.filter((item:number):boolean =>item>3);  //[4,5]
    console.log(filter);

    //求和
    const total:number = items4.reduce((total:number,cur:number):number=>total+cur,0);
    console.log(total)   
    
    //类型别名
    function myHello(person: n) {
        return 'Hello, ' + person;
    };
    type n = number;
    let name:n = 18;
    console.log(myHello(name));
}

二、类的实现

1. 类的定义

window.onload=function(){
    //类的定义,类的类型,类继承的类型
    class Cat1 {
        //属性声明
        name:string;
        color:string;
        constructor(name:string,color:string){
            this.name = name;
            this.color = color;
        }
        eat(v:string):string {
            return v;
        }
    };
    let c1 = new Cat1('jack','black');
    console.log(c1.name) //jack

    //类受到接口的约束
    interface IFeatures {
        name:string;
        age:number;
        action?():string;
    }

    //类实现接口
    class Person implements IFeatures {
        name:string;
        age:number;
        msg:string;
        constructor(name,age){
            this.name = name;
            this.age = age;
            this.msg = 'hello'
        }
        action(){
            return 'action'
        }
    };
    let p1 = new Person('john',20);

    //类继承 实现
    //场景:
    //1、车对象  拥有特性:报警的功能+灯光系统
    //2、门对象 有子对象,防盗门 防火门。。  拥有特性:报警的功能
    //报警的功能
    interface IAlarm{
        sing(bell:string):string;
    }

    //灯光系统
    interface ILight {
        lightOn();
        lightOff():boolean;
    }

    //定义类对象
    //门
    class Door {
        type:string;
        constructor(type){
            this.type = type;
        }
    };

    //防盗门 继承门的特性 实现报警的功能
    class SecurityDoor extends Door implements IAlarm{
        constructor(type:string){
            super(type)
        }
        sing(bell){return bell}
    }
    let s1 = new SecurityDoor('abc');
    s1.type
    s1.sing('dididi');

    //车 拥有特性:报警的功能+灯光系统
    class Car implements IAlarm, ILight{
        height:number;
        color:string;
        constructor(height,color){
            this.height = height;
            this.color = color;
        }
        sing(v){return v}
        lightOn(){}
        lightOff(){return true}
    };
}

2. 修饰符

window.onload=function(){
    //public 修饰的属性或方法是公有的,可以在任何地方被访问到,默认所有的属性和方法都是 public 的;
    class Cat1 {
        //属性声明
        public name:string;
        public color:string;
        constructor(name:string,color:string){
            this.name = name;
            this.color = color;
        }
        public eat(v:string):string {
            return v;
        }
    };
    let c1 = new Cat1('jack','black');
    console.log(c1.name) //jack
    c1.eat('eat')

    //private 修饰的属性或方法是私有的,不能在声明它的类的外部访问;
    //私有的 只能在类内部访问,子类或类的实例都无法访问
    class Cat2 {
        //属性声明
        private name:string;
        public color:string;
        constructor(name:string,color:string){
            this.name = name;
            this.color = color;
        }
        private say():void {
            console.log(`my name is ${this.name}`) //只能在类内部访问
        }
    };
    let c2 = new Cat2('jack','black');
    //c2.name  //error
    //c2.say()

    class Test extends Cat2{
        constructor(name:string,color:string){
            super(name,color)
        }
        action(){
            console.log(this.name)  //报错  子类无法访问
        }
    };
    let t1 = new Test('jack','black')
    

    //protected 修饰的属性或方法是受保护的,它和 private 类似,区别是它在子类中也是允许被访问的;
    //受保护的 子类中也是允许被访问的,但不能在类的实例中访问
    class Cat3 {
        //属性声明
        protected name:string;
        public color:string;
        constructor(name:string,color:string){
            this.name = name;
            this.color = color;
        }
        protected say():void {
            console.log(`my name is ${this.name}`) //只能在类内部访问
        }
    };
    let c3 = new Cat3('jack','black');
    c3.name; //报错 不能在类的实例中访问

    class Test3 extends Cat3{
        constructor(name:string,color:string){
            super(name,color)
        }
        action(){
            console.log(this.name)  //子类可以访问
        }
    };
    let t3 = new Test3('jack','black')

    //扩展
    class Employee {
        //属性声明
        protected salary:number;
        public name:string;
        constructor(name:string,salary:number){
            this.salary = salary;
            this.name = name;
        }
        public say():number {
            console.log(this.salary);
            return this.salary
        }
    };
    let employee1 = new Employee('jack',10000);
    //employee1.salary //报错
    employee1.say()  //10000

    //4、static 静态方法,作用:不需要实例化,直接通过类来调用
    class Employee2 {
        //属性声明
        static salary:number=20;
        public name:string;
        constructor(name:string,salary:number){
            this.name = name;
        }
        static say() {
            console.log(Employee2.salary)
        }
    };
    //let employee2 = new Employee2('jack',10000);
    //employee2.say(); //报错
    Employee2.say(); //20 不需要实例化,直接通过类来调用


    //5、只读修饰符readonly
    class Employee3 {
        //属性声明
        readonly salary:number=2000;
        public name:string;
        constructor(name:string,salary:number){
            this.name = name;
            this.salary = salary;  //只有在类的构造函数中才能进行赋值修改
        }
        public say() {
            this.salary = 20000; //由于是只读,不能修改
        }
    };
    let employee3 = new Employee3('jack',10000);
    employee3.salary = 50000; ////由于是只读,不能修改
}

三、泛型

window.onload=function(){
    //泛型
    //数组泛型 Array<类型>
    //为什么要泛型
    function fn1(a:number,b:number):number[]{
        return [a,b]
    };
    fn1(10,20);

    function fn2(a:string,b:string):string[]{
        return [a,b]
    };
    fn2('hello','fn2');

    function fn3(a:boolean,b:boolean):boolean[]{
        return [a,b]
    };
    fn3(true,false);

    //类型是变量T
    function fn4<T>(a:T,b:T):T[]{
        return [a,b]
    };
    fn4<number>(10,20);
    fn4<string>('hello','fn2');
    fn4<boolean>(true,false);

    //扩展
    // function fn5<T>(str:T):number{
    //     return str.length
    // };
    // fn5<string>('hello');
    
    //解决:泛型约束 extends
    //泛型变量T进行约束  限制泛型的类型 使用接口
    interface ILength {
        length:number;
    }
    //传入的类型必须有length属性
    function fn6<T extends ILength>(str:T):number{
        return str.length
    };
    fn6<string>('hello');
    fn6<number>(100); //error
    fn6<number[]>([1,2,3,4,5])
    fn6<Array<number>>([1,2,3,4,5]);
    fn6<ILength>({length:10})
    fn6<{length:number,id:number}>({length:10,id:1})


    //扩展
    interface INamed {
        name:string;
        [propName:string]:any;
    };
    //传参数必须有name属性
    function getName<T extends INamed>(obj:T):string{
        return obj.name
    };
    getName<INamed>({name:'tom',age:18})
    getName<INamed>({title:'tom',age:18})
   

    //泛型在函数中的写法
    //函数声明
    function f7<T>(n1:T,n2:T):T[]{
        return [n1,n2]
    }
    //函数表达式
    let f8 = function<U>(n1:U,n2:U):Array<U>{
        return [n1,n2]
    }

    //箭头函数
    let f9 = <U>(n1:U,n2:U):Array<U>=>[n1,n2];

    //多个参数
    interface IMultiple<T,U>{
        first:T;
        second:U;
    }
    const multiple1:IMultiple<number,string> = {
        first:1,
        second:'two'
    }
    const multiple2:IMultiple<boolean,number> = {
        first:true,
        second:100
    }
    
    //扩展
    let f10 = <N,S>(arr:[N,S]):[S,N]=>[arr[1],arr[0]];
    f10<number,string>([100,'hello']);

    let f11 = <N,S>(n1:N,n2:S):[S,N]=>[n2,n1];
    //let f12 = <泛型变量1,泛型变量2>(形参1,形参2):返回值类型=>函数体,返回结果;


    //泛型接口:在定义接口时使用泛型类型
    //具体理解
    //定义函数1
    // let fun1 = function(s1,s2){
    //     return s1===s2
    // };
    // fun1(100,'100');
    
    // //定义函数2
    // let fun2 = function(x,y){
    //     if(x>y){
    //         return true;
    //     }else {
    //         return false;
    //     }
    // };
    // fun2(100,200);

    //以上函数逻辑不同,类型有什么不一样
    // //定义函数1
    // let fun1 = function(s1:number,s2:number):boolean{
    //     return s1===s2
    // };
    // fun1(100,100);
    
    // //定义函数2
    // let fun2 = function(x:number,y:number):boolean{
    //     if(x>y){
    //         return true;
    //     }else {
    //         return false;
    //     }
    // };
    // fun2(100,200);
    //共同点:传入二个参数,返回boolean
    // interface IInspectFunc {
    //     (a:number,b:number):boolean;
    // }
    // //定义函数1
    // let fun1:IInspectFunc = function(s1,s2){
    //     return s1===s2
    // };
    // fun1(100,100);
    
    // //定义函数2
    // let fun2:IInspectFunc = function(x,y){
    //     if(x>y){
    //         return true;
    //     }else {
    //         return false;
    //     }
    // };
    // fun2(100,200);

    //为什么要用到接口?复用+抽离+灵活+扩展+简洁。。。
    //泛型用到了接口中  泛型接口:在定义接口时使用泛型类型
    // interface IInspectFunc {
    //     <T>(a:T,b:T):boolean;
    // }
    // //定义函数1
    // let fun1:IInspectFunc = (s1,s2)=>s1===s2;
    // fun1<string>('100','120');
    
    // //定义函数2
    // let fun2:IInspectFunc = (x,y)=>x>y?true:false;
    // fun2<number>(100,200);

    //另一种写法:泛型参数提前到接口名上
    interface IInspectFunc<T> {
        (a:T,b:T):boolean;
    }
    //定义函数1
    let fun1:IInspectFunc<string> = (s1,s2)=>s1===s2;
    fun1('100','120');
    
    //定义函数2
    let fun2:IInspectFunc<number> = (x,y)=>x>y?true:false;
    fun2(100,200);

    //扩展2 定义一个函数,传入长度和值,返回数组(数组的长度是传入的长度,数组的值传入的参数)
    // let createArray = function<T>(len:number,v:T):Array<T>{
    //     let arr:T[]= [];
    //     for(let i=0;i<len;i++){
    //         arr[i] = v;
    //     };
    //     return arr;
    // }
    // createArray<string>(5,'hello'); //['hello','hello','hello','hello','hello']
    // createArray<number>(3,100); //[100,100,100]

    //抽离 输入类型和输出类型
    interface ICreateArr {
        <T>(len:number,v:T):Array<T>
    }

    let createArray :ICreateArr = function<T>(len,v){
        let arr:T[]= [];
        for(let i=0;i<len;i++){
            arr[i] = v;
        };
        return arr;
    }
    createArray<string>(5,'hello'); //['hello','hello','hello','hello','hello']
    createArray<number>(3,100);

    //另一种写法
    let createArray2:ICreateArr = <T>(len,v)=>Array<T>(len).fill(v);
}

四、类型断言

window.onload=function(){
    //type 和interface的区别
    //type 类型别名  为类型起新的名字
    type abc = string;
    let s:abc = 'hello';  //===?let s:string = 'hello'

    type xyz = number|string|boolean;
    let a:xyz = 100;

    let fn = (v:xyz):abc=>'v';

    type Person = {
        name:abc;
        age:number;
    };
    let o1:Person = {
        name:'o1',
        age:20
    }

    let o2:{name:string;age:number}= {
        name:'o1',
        age:20
    }

    //支持字面量表示
    type Shape = {
        kind?:'circle';
        radius:number;
    };

    const circle:Shape = {kind:'circle',radius:10};
    //const circle:Shape = {kind:'abc',radius:10};

    //type定义联合类型
    type Shape2 = {
        kind:'circle';
        radius:number;
    } | {
        kind:'square';
        weight:number;
    };
    const circle2:Shape2 = {kind:'square',weight:10};
    const circle3:Shape2 = {kind:'circle',radius:10};

    //交叉类型
    type TName = {
        name:string;
    }
    type TAction = {
        action(v:string):void
    }
    //同时二个type
    type Test = TName & TAction;
    const o5:Test = {
        name:'john',
        action(v:string):void{
            console.log(`${v} ${this.name}`)
        }
    };
    o5.action('hello ')

    //interface和type ,interface会合并,type不同
    interface IPerson {
        name:string;
    }
    interface IPerson {
        age:number;
    };
    const p:IPerson ={
        name:'p',
        age:20
    }
}

五、枚举

window.onload=function(){
    //枚举
    //enum类型是对JavaScript标准数据类型的一个补充
    let arr = ['red','green','blue'];
    arr['red'] //error

    let o1 = {
        red:0,
        green:1,
        blue:2
    };
    //o1.red o1['red']
    o1[0] //error

    //枚举定义
    //enum 枚举名称 {成员1,成员2,成员3}
    enum Color {Red,Green,Blue};
    //获取属性名获取
    //console.log(Color.Red)  //0 获取索引
    //成员值0,转换为对应的属性名称
    console.log(Color[0])  //'Red'
    //总结:枚举类型可以视为具有二个属性的对象:名称到值的映射、值到名称的映射;

    //作为类型添加
    let c1:Color = Color.Blue;
    let c2:string = Color[0];
    //修改枚举类型
    //Color.Green = 5; //不能修改,是只读

    //具体使用场景
    enum Day {
        Sunday,
        Monday,
        Tuesday,
        Wednesday,
        Thursday,
        Frisday,
        Saturday
    };
    //函数的返回值
    function fn():Day{
        let n = new Date().getDay(); //5
        if(n===Day.Saturday ||n===Day.Sunday ){
            console.log('休息')
        }else {
            console.log('上班')
        }
        return n;
    };
    fn()

    //扩展
    enum Color2 {Red,Green,Blue};

    function getColorName(color:Color2):string{
        switch (color){
            case Color2.Red:
                return '红色';
            case Color2.Green:
                return '绿色';
            case Color2.Blue:
                return '蓝色';
            default:
                return '其它颜色'
        }
    };
    getColorName(2)
   // getColorName(3) //error

   //更多灵活性
   //1、数字枚举
   enum Color3 {
        Red = 1,
        Green = 10,
        Blue = 15
    };
    //console.log(Color3[10]) //'Green'
    console.log(Color3.Blue) //15

    //2、小数枚举
   enum Color4 {
        Red = 1.6,
        Green ,
        Blue
    };
    console.log(Color4.Blue) //3.6

    //4、字符串枚举
   enum Color5 {
        Red = 'red',
        Green = 'green',
        Blue= 'blue'
    };
    console.log(Color5.Blue) ///'blue'

    //5、任意值枚举
    let count = 888;
   enum Color6 {
        Red = 'red',
        Green = 'green'.length,
        Blue= <any>count
    };
    console.log(Color6.Blue) //888
}

六、元祖

window.onload=function(){
    //元组
    //数组合并了相同类型的对象,而元组(Tuple)合并了不同类型的对象。
    //数组
    let arr1:number[] = [1,2,3,4,5];
    let arr2:Array<string> = ['a','b','c'];
    let arr3:(number|string)[] = [1,2,3,'4','5'];
    let arr4:Array<any> = ['a',100,true];

    const items = ['a',100,true];
    let arr5:Array<any> = items;

    //元组的定义
    let tuple1:[string,number,boolean] = ['a',100,true];

    //常用的写法
    let tuple2:[number,string] =[100,'hello'];
    let tuple3:[number,[number,number]] =[100,[1,2]];
    let tuple4:[number,Array<number>] =[100,[1,2,3,4,5]];

    //对象
    interface IPerson{
        name:string;
        age:number;
    };
    let o1 :[IPerson]= [{name:'o1',age:20}];
    let o2 :IPerson[]= [{name:'o1',age:20}];
    let o3 :[IPerson,boolean,Array<number>]= [{name:'o1',age:20},true,[1,2,3,4]];

    //使用场景:
    let person:[string,number] = ['John',20];

    //扩展:
    //处理多个返回值,定义数组,返回最小值和最大值
    let numbers:Array<number> = [5,2,10,8,3];
    function fn(arr:number[]):[number,number]{
        let min = arr[0];
        let max = arr[0];
        for(const n of arr){
            if(n < min){
                min = n;
            };
            if(n>max){
                max = n;
            };
        };
        return [min,max];
    }
    const [min,max] = fn(numbers);
    console.log(min);
    console.log(max);
}