TypeScript学习笔记

1. 数据类型

var age:number = 10;
// 无法重新声明块范围变量“name”。ts(2451)
// 如果代码里有export  import  之类的代码,n那么这么文件变成一个模块
export { }   //不加这个会覆盖全局变量
let name: string = "zhufeng";
let married: boolean = true;
let hobbies: string[] = ["1","2"];   //声明字符串数组  第一种
let interests: Array<string> = ["4","5"]   //声明字符串数组 第二种

// 元组:  类似数组,但是他是一个长度和类型都固定的数组
// 1  长度固定  2 类型可以不一样

let point: [number, number] = [100,200];

let person: [string, number] = ["zhufeng",200];


//枚举
enum Gender {
    BOY,
    GIRL
}
console.log(`${Gender.BOY}`);

/* 
    var Gender;
    (function (Gender) {
        Gender[Gender["BOY"] = 0] = "BOY";
        Gender[Gender["GIRL"] = 1] = "GIRL";
    })(Gender || (Gender = {}));
    console.log("\u4F60\u662F" + Gender.BOY); 
*/
let Gender2 = {
    0: "BOY",
    1: "GIRL",
    "BOY": 0,
    "GIRL": 1
}

enum WEEK {
    mondy = 1,
    tuesday = 2
}
console.log(WEEK.mondy);

// 任意类型  anyscript 
//  第三库没有类型定义  类型转换的时候  数据结构太复杂太灵活的时候用
// ts  为 dom提供了一整套类型声明
let root: HTMLElement | null= document.getElementById('root');
root!.style.color = 'red';   //  !:强行断言,断言不为空

// null undefined
// null空  undefined   未定义
// 他们都是其他类型的子类型  可以把它们赋值给其他类型的变量
let myname: string = null;   //配置文件改 "strictNullChecks": false,   允许赋值为null和undefined
let x: number;
x = 1;
x = undefined;
x = null;


// void  空的  没有,
function greeting(name: string): void{   //不能有返回值
   console.log(`hello ${name}`);
//    return 1  报错
   return null //或者返回undefined  因为null undefined是void的子类型
}
greeting("zhufeng")

// never  永远不
// 是其他类型的子类型,代表永远不会出现的值
// 返回“从不”的函数不能具有可访问的终结点。
// 一个从来不会有返回值的函数(如:如果函数内含有 while(true) {});
// 一个总是会抛出错误的函数(如:function foo() { throw new Error('Not Implemented') },foo 的返回类型是 never);
// 情况一:如果启动一个后台任务,那么他就是一个死循环,事件环函数  计划任务(如每隔一个半小时执行一次)
// 情况二: 

// 1.在函数内部永远抛出错误,导致函数永远无法正常结束
function createError(message: string): never{
    console.log(1);
    throw new Error("error")
    console.log(2);
}

// 2.永远不会出现的值,永远触达不到结束点
function sum():never {
    while(true){
        console.log('hello');
        
    }
    console.log('end point');
    
}
/* 与 void 的差异
一旦有人告诉你,never 表示一个从来不会优雅的返回的函数时,
你可能马上就会想到与此类似的 void,然而实际上,
void 表示没有任何类型,never 表示永远不存在的值的类型。
当一个函数没有返回值时,它返回了一个 void 类型,
但是,当一个函数根本就没有返回值时(或者总是抛出错误),
它返回了一个 never,void 指可以被赋值的类型(在 strictNullChecking 为 false 时),
其他任何类型不能赋值给 never,除了 never 本身以外。 */


// 3.12 包装对象  java装箱和拆箱
// 自动在基本类型和对象类型进行自动切换
// 1.基本类型上没有方法
// 2.在内部迅速完成一个装箱的操作,把基本类型迅速包装成对象类型,然后用对象来调用方法
let name4 = 'zhufeng';
// name4.toLocaleLowerCase();
let name44 = new String(name4);
name4.toLocaleLowerCase();

/* let isOk1:boolean = true;
let isOk2:boolean = Boolean("123");  //任何类型值传给函数,会返回一个基本类型的true或false
let isOk3:boolean = new Boolean(1);  //报错,因为new就成对象了,不能把对象赋值给基本类型 */



// 3.13联合类型  Union Types  表示取值可以为多种类型中的某一个
// 未赋值时只能访问两个类型共有的属性和方法
let name5: string | number;
name5 = 'zhufeng';
name5.toLocaleLowerCase();  //只能调用字符串方法

name5 = 5;
name5.toFixed(2);  //只能调用数字上的方法


// 3.14类型断言
// 讲一个联合类型的变量,指定为一个更加具体的类型
let name6: string | number;
(name6 as string).toLocaleLowerCase()  //把他断言当成一个字符串,就能调用字符串的方法了
//   字面量类型
type Gender4 = 1 | 'Boy' | 'girl';
let a:Gender4 = 'Boy';
/* Gender4 = 'girl';
Gender4 = 'ren';  */
// 


2. 函数

// 函数定氮仪
function hello(name: string){
    console.log(name);
    
}
// type用来定义一个类型或者  类型别名
/* type GetUserNameType = (firstName:string, lastName: string) => string   //这不是箭头函数,是个对象  =>:分隔符*/
type GetUserNameType = (firstName:string, lastName: string) => {
    name: string
}

// 函数表达式
/* let getUserName: GetUserNameType  = function(firstName:string, lastName: string) :string {
    return firstName + lastName
} */
let getUserName: GetUserNameType  = function(firstName:string, lastName: string) : {
    name:string
}{
    return { name: firstName + lastName}
}

// 返回空
type GetUserNameType1 = (firstName:string, lastName: string) => void
let getUserName1: GetUserNameType1  = function(firstName:string, lastName: string) : void{

}


// 可选函数(传的实参或者形参个数可以不一样) "?"
function print(name:string, age:number, home?:string){
  
}
print("zhufeng",10)

// 默认参数
function ajax(url:string, method: string = 'get'){
   console.log(url,method);
}
ajax('/url')

// 剩余参数
function sum(...numbers: Array<number>){
  return numbers.reduce((total, item) => total + item, 0);
}

// 函数重载 
//   overload(重载),限定函数调用的格式
let obj: any = {};

// 函数声明,用来限定attr的传参
function attr(val: string):void;
function attr(val: number):void;
/* function attr(val: string | number) :void;  这种写法和上面两行一样的效果,但是上面更精确*/
// console.log();  //报错。函数实现缺失或未立即出现在声明之后,上下不能分开

// 第一种情况
function attr(val: any):void{
  if(typeof val == 'string'){
      obj.name = val
  } else if (typeof val == 'number'){
    obj.age = val
  }
}
attr('zhufeng')
attr(10)
/* attr(true)   //报错,因为限定了string和number */

// 第二种情况
function sum2(a: string, b:number): void;
function sum2(a: number, b:number): void;
function sum2(a:any, b:any): void{

}
// sum2("1","2")
sum2("2",3)
/* sum2("2","5")   //报错 */




3. 类

// 如何定义类

// namespace:命名空间,放这里类名可以重复
namespace a{
    class Person {
        name: string = "zhufeng";
        age: number;
        constructor(){
            this.age = 10;
        }
    }
    let p1 = new Person();
/*     console.log(p1.name);
    console.log(p1.age); */
}




// 读取器   getter  setter
namespace b{
    class Person {
        myname: string = "zhufeng";
        constructor(name: string){
            this.myname = name;
        }
        get name(){
            return this.myname;
        }
        set name(newVal: string){
            this.myname = newVal.toUpperCase();
        }
    }

    let p = new Person('zhufeng');
    // console.log(p.name);
    p.name = 'jiagou'
    // console.log(p.name); 
}

namespace c {
    class Person{
        // myname: string = "zhufeng";   public相当于这句,挂载到实例上就是公共的
        constructor(public readonly name: string){   //readonly只读属性

        }
    }
    let p = new Person("zhufeng")
    // p.name = "44"  //无法分配到 "name" ,因为它是只读属性。
    console.log(99,p.name);
}

// 继承
/* 
* 子类继承父类
*  访问修饰符  public protected  private
*         public     公共的   自己  自己的子类  和其他类都能访问
*         protected  受保护的 自己和自己的子类可以访问
*         private    私有的  只能自己访问,自己的子类不能访问
*/
namespace d {
    class Person {
        public name: string;
        protected sex: string;
        protected age: number;
        private amount: number;
        constructor(name: string, age: number,sex: string){
            this.name = name;
            this.age = age;
            this.sex = sex;
            this.amount = 10;
        }
        getName(){
            return this.name;
        }
        setName(newName: string){
            this.name = newName;
        }
    }
    class Student extends Person{
        static type  = "student"    //说明是类的属性,不是实例(原型上)的属性,调用方式:【类名.属性名】  
        stuNo: number;
        constructor(name: string, age: number,stuNo: number,sex: string){
            super(name,age,sex);
            this.stuNo = stuNo;
        }
        static getType(){
            console.log("测试static");
            return Student.type
        }
        getStudentNo(){
            return this.stuNo + this.sex ;
          
        }
        setStudentNo(newStuNo: number){
            this.stuNo = newStuNo;
        }
        
        
    }

    let s = new Student("zhufeng", 10, 1,"男00")
    console.log(s.name);    //s可以访问到name属性,因为是public,
    console.log(s.sex);    //s访问不到,报错 
   console.log(Student.type);
   Student.getType();
   
}

// static 静态属性  静态方法   见81行   详情截图见md文件 
// 需要修饰符static  类名来访问属性   无法被实例继承



4. 装饰器

// ###  装饰器(东西挺多)

// 5.8  装饰器(实际上就是一个函数)
// 写法分为  普通装饰器(不传参数)  和   装饰器工厂(传参数)

// 5.8.1类装饰器:用来声明,用来监视、修改或替换类定义

// 装饰器执行顺序:属性和方法执行(谁先写谁执行) 》 方法(先参数再方法,而且  他们一定会在一起) 》 类(如果是同类型的,先执行后写的(也可以理解为谁距离class Person近就先执行谁))

// 类装饰器
namespace s{
    interface Person{    //强化
        xx: string;
        yy: string;
    }
    function enhancer(target: any){
        // target.xx = "Person"   这样写 p.xx  是undefined,因为target是实例,所以得在原型省上加属性
        target.prototype.xx = "大写的XX";
        target.prototype.yy = "大写的YY"
    }

    @enhancer   //随便起的名字
    class Person {
        constructor(){

        }
    }
    let p = new Person();
    console.log(p.xx);    //实例的属性
    console.log(p.yy); 
    // console.log(Person.xx);   //类的属性  
}


// 把类整个替换 (相当于子类继承父类,达到增强父类的效果)
namespace b{
    /* function enhancer(name: string){ */
    function enhancer(){
        return function enhancer(target: any){    //返回一个装饰器函数,装饰class函数
           /*  return function (target: any){
                return class extends Person{
                   constructor(age:number,name:string){
                       super(age,name);
                    //    this.name = name;
                   }
                   getAge(){
                       console.log(this.age);
                   }
                } */
            return class extends target  {
                public name: string = "sss";
                public age: number = 10;
            }  
        }
    }
    @enhancer()       //这可以传参,
    class Person {
        // public name: string = "person";
        public age:number = 10;
        public name:string = 'zhufeng';
        constructor(){ }
    }
    let p = new Person();
    console.log('000',p.name);   //sss
    console.log(p.age);    //10
    // p.age     //会报错,因为Person里没有
       //这里的p指向的是enhancer里的class类,和class Person这个类已经没关系了
}


// 属性装饰器  装饰普通属性,普通方法,类的属性,类的方法
namespace c {
    // target如果装饰的是普通属性的话,target指向类的原型
    // target如果装饰的是类的属性static,target指向类的定义
    function upperCasr(target: any, propertypeName: string) {    //修饰属性有两个参数
        let value = target[propertypeName];
        const getter = () => value;
        const setter = (newVal: string) => {
            value = newVal.toUpperCase();
        }
        delete target[propertypeName]
        Object.defineProperty(target, propertypeName, {
            // 属性描述器
            get:getter,
            set:setter,
            enumerable: true,   //是否可枚举
            configurable: true  //是否可配置
        })
    }
    function propertyEnumerable(flag: boolean){   //属性
        return function (target: any, methodName: string){   //如果修饰属性的话传两个参数
            // propertyDescriptor.enumerable = flag;
        }

    }
    function methodEnumerable(flag: boolean){   //方法
        return function(target: any, methodName: string, propertyDescriptor: PropertyDescriptor){
            propertyDescriptor.enumerable = flag;
        }
    }
    function setAge(age: number){   //方法
        return function(target: any, methodName: string, propertyDescriptor: PropertyDescriptor){
            target.age = age
        }
    }
    function toNumber(target: any, methodName: string, propertyDescriptor: PropertyDescriptor){   //装饰个方法
        let oldMethod = propertyDescriptor.value;   //这是指向一个老的方法sum函数
        propertyDescriptor.value = function (...args:any[]){
            args = args.map(item => parseFloat(item))
            return oldMethod.apply(this,args)
        }
    }
    class Person {
        static age: number;
        @upperCasr
        @propertyEnumerable(false)
        name: string = 'zhufeng'

        @methodEnumerable(true)   //可枚举
        getName(){     //如果修饰的是普通方法的话,他的修饰器的target指向类的原型
            console.log("getName");
            
        }
        
        @setAge(100)
        static getAge(){   //如果修饰的是static静态属性的话,他的修饰器的target指向类

        }
        
        @toNumber
        sum(...args: any[]){
            return args.reduce((accu, item) => accu + item,0)

        }
    }

    let p = new Person();
    p.name = 'jiagou';
    console.log(p.name);
    for(let attr in p){
        console.log('attr=' + attr);
    }
    console.log(Person.age);
    console.log(p.sum(1,'2','3'));
    
}

// 参数装饰器   装饰方法参数的   (很少用)
namespace d {
    interface Person{
        age: number
    } 
    // Person.protype(指向类的原型) login 1
    function addAge(target: any,methodName:string,paramsIndex: number){   //参数的装饰器
        // console.log(target,methodName,paramsIndex);
        target.age = 12;
    }
    class Person{
        login(username: string, @addAge pwd: string){
            console.log(this.age,username,pwd);
        }
    }
    
    let p = new Person();   //实例化
    p.login("str","123")
}

5. 抽象类




namespace a{
    abstract class Animal {
        name: string;
        abstract getName():string
    }   
    class Cat extends Animal {
        getName():string {
            return this.name
        }
    }
    // let Animal = new Animal();   报错   父类不能实例化
    let cat = new Cat();   //子类可实例化
    cat.name = "mao";
    console.log(cat.getName());


    // 1.用来描述对象的,指的是对象有哪些属性,属性什么类型
    interface Point {
        x:number
        y:number
    }
    let point: Point = {x:0, y:0}

    // 2.还可以用来描述行为的抽象
    interface Speakable {
        speak(): void
    }
    interface Eatable {
        eat(): void
    }
    // 一个类可以实现多个接口,但只能继承一个父类
    class Person implements Speakable, Eatable {
        speak(){}
        eat(){}
    }
}


namespace b {
    // 重写   子类重新实现并覆盖父类中的方法
    class Animal{
        constructor() {

        }
        speak(){
            // throw new Error("此方法不能调用")
            console.log('动物叫');
        }
    }

    class Cat extends Animal{
        speak() {
            console.log('///我们一起喵喵喵');
            super.speak()   //动物叫
            console.log(567);
            
        }
    }
    let p = new Cat();
    p.speak();   //=>我们一起喵喵喵

    class Dog extends Animal{
        speak() {
            console.log('我们一起汪汪汪');
            super.speak()   //调用父类方法
        }
    }
    let dog = new Dog();
    dog.speak();   //=>我们一起汪汪汪   =>相当于子类重写了父类的speak方法  如果子类想调父类方法的话就用super.speak()(实际上只是把父类方法隐藏了,并不是干掉了)
    // super: 指向父类的原型   super.speak()是个整体  super.speak()  转译后= > Animal.prototype.speak()
}

6. super

namespace a{
    // class中的super,有两种指向,在静态方法和构造函数,指向父类,在普通函数中,指向父类的prototype
    // 关于继承跟静态没有关系,子类并不能继承父类的静态方法
    abstract class Animal {
        name: string;
        abstract getName():string
    }   
    class Cat extends Animal {
        getName():string {
            return this.name
        }
    }
    // let Animal = new Animal();   报错   父类不能实例化
    let cat = new Cat();   //子类可实例化
    cat.name = "mao";
    console.log(cat.getName());
}

7. 接口 - 任意属性

// 任意属性

namespace a {
    interface PlainObject {
        x:number,
        y:number
        [propName: string]: number;
    }
    let obg: PlainObject = {
        x: 1,
        y: 2,
        z: 3
    }

    // 接口de继承
    interface Speakable {
        speak(): void;
    }
    interface SpeakChinese extends Speakable {
        speakChinese() :void
    }
    class Person implements SpeakChinese {
        speak() {}
        speakChinese() {}
    }

    // 接口的只读
    interface Circle{
        readonly PI: number;
        radius: number
    }
    let circle:Circle={
        PI: 3.14,
        radius: 10
    }
    // circle.PI = 3.14   //报错

    // 接口还可以用来约束函数
    interface Discount{
        (price:number): number
    }
    let cost: Discount = function (price: number): number {
        return price * .8 
    }

    // 可索引接口
    // 用来对数组和对象进行约束
    interface UaerInterface {
        [index: number]: string
    }
    let arr: UaerInterface = ['1','2','3']
    console.log(arr);
    let obj2: UaerInterface = {
        1:'1',
        2:'2'
    }
    console.log(99,obj2);

    // type  interface
}

namespace b{
    // 类接口  可以用接口来装饰类;
    //  学ts核心  接口+泛型 

    interface Speakable {
        name?: string,
        speak(words: string) :void
    }
    class Dog implements Speakable{
        name:string;
        speak() {
            console.log('hahah');
        }
    }

    let dogg = new Dog();
    dogg.speak()

    // 用接口来约束类   约束构造类型,使用new来约束
    class Animal {
        constructor(public name: string){
            console.log(name);
        }
    }
    interface WithNameClass {
        new(name: string): Animal
    }
    function createAnimal(clazz: WithNameClass, name: string){
        return new clazz(name)   //new了clazz,传参name
    }

    let a = createAnimal(Animal, 'zhufeng')
    console.log(a);




    
    interface SquareConfig {
        color?: string;
        width?: number;
      }
    function createSquare(config: SquareConfig): {color: string; area: number} {
        let newSquare = {color: "white", area: 100};
        if (config.color) {
          newSquare.color = config.color;
        }
        if (config.width) {
          newSquare.area = config.width * config.width;
        }
        console.log(config.width * config.width);
        
        return newSquare;
      }
      
      let mySquare = createSquare({color: "black"});
      console.log(mySquare);
      
    
}

8.泛型

// 泛型(Generics)是指在定义函数、接口或类的时候,不预先指定具体的类型,而在使用的时候再指定类型的一中特性
//                作用域只限于函数内部使用
// 函数泛型  类数组泛型  类泛型 

namespace a{
    // 普通函数
    function creatArray111(length: number, val: any):Array<any>{   
        let result: Array<any> = [];
        for(let i = 0; i < length; i++){
            result[i] = 1;
        }
        return result;
    }
    var aa3 = creatArray111(3,3);
    console.log(aa3);   //any无法控制   具体类型不灵活,所以
//  ==========================================改用泛型

    

    function creatArray<T>(length: number, val: T):Array<T>{   //val什么类型,返回什么类型的数组  T占位符,相当于个模板(参数)
        let result: Array<T> = [];
        for(let i = 0; i < length; i++){
            result[i] = val;
        }
        return result;
    }

     function create<T>(length: number, val: T): Array<T>{
       
     }

     }
    setTimeout(()=>{
        let result1 = creatArray<number>(3, 3);   //开辟另一个新的堆内存空间,无所谓异步
        console.log(result1);
    })

    let result2 = creatArray<string>(3, "zhufeng");  //开辟另一个新的堆内存空间,和上面result1没有任何关系,相当于参数
    console.log(result2);

    // 类数组泛型
    function sum(...args2: any[]){    //: any[]:因为类型不固定,
        let args:IArguments = arguments;   //类型:类数组IArguments  ts语言本身自带的,实力上也是个接口
        for(let i = 0;i<args.length;i++){
            console.log(args[i]);
        }
    }
    sum(1,2,"3")

    
    //以下三个类型都是固定的   
  /*   // document is not defined  改tsconfig.json文件中的"lib":   //!:非空断言,告诉他一定不为null
    let root: HTMLElement | null = document.getElementById('root');   //类型:HTMLElement | null
    let children: HTMLCollection = root!.children;
    let childrenNodes: NodeListOf<ChildNode> = root!.childNodes; */



    // 类泛型
    class MyArray<T>{
       private list: T[] = [];
       add(val: T){
          this.list.push(val)
       }
       getMax(): T {
           let result: T = this.list[0];
           for(let i = 0; i < this.list.length; i++){
                if(this.list[i] > result) {
                    result =  this.list[i]
                }
           }
           return result;
       }
    }
    let arr = new MyArray<number>();
    arr.add(1);
    arr.add(2);
    arr.add(3);
    let result3: number = arr.getMax();
    console.log(result3);
    

    // 接口泛型
    interface Calculate {
        <T>(a: T,b: T): T   //<T>相当于定义变量   a: T相当于调用变量
    }
    let add: Calculate = function <T>(a: T,b: T): T{
        return a
    }
    let result4 = add<number>(5,5)  
    console.log(result4);

    interface Caluclate{
        <T>(a: T,b:T): T
    }
    let addz: Caluclate = function (a,b){
        return a
    }
    let result8 = add<number>(5,5)  
    console.log(999,result8);


    // 多个类型参数  如何在不知能加中间变量的情况下,交换两个变量的值
    function swap<A, B>(tuple: [A, B]): [B, A]{
        return [tuple[1], tuple[0]]
    }
    let aa = swap<string, number>(["zhufeng",10])
    console.log(aa);

    function swapz<A, B>(aa:[A,B]): [B,A]{
       return [aa[1],aa[0]]
    }
    let result5 = swapz<string,number>(['zhufeng',40])
    console.log(result5);
    





    // 默认泛型
    function creatArray2<T = string>(aa: number): T {
        let t: T | null = null;
        return t
    }
    let result6 = creatArray2<number>(3);
    // let result6 = creatArray2(3);    没传泛型的话走默认的string类型

    console.log(result6);



    // 泛型的约束
    // 在函数使用泛型的时候,由于预先并不知道具体的类型,所以不能访问相应类型的具体方法
    // 小技巧:意思就是想要调用泛型上的方法的话,就写一个接口,让泛型继承这个接口,就可以调用接口上的方法了
    interface LengthWith {
        length: number
    }
    function logger<T extends LengthWith>(val: T){

        console.log(val.length);
    }
    logger('zhufeng')


    // 也可以放接口上
    interface Cart<T>{
       list: T[]
    }
    let cart: Cart<string> = {
        list:['1','2','3']
    }
    

    
    // 泛型类型别名
    type Cart2<T> = {list: T[]} | T[];
    let c1: Cart2<string> = { list: ['1']};
    let c2: Cart2<string> = ['1'];

    // interface  定义一个实实在在的接口,他是一个真正的类型
    // type 一班用来定义别名,并不是一个真正的类型(小名 )
    
}

9.接口兼容性

namespace a{
    // 接口兼容性(重新赋值不报错,类型自动转化)     在ts里和类型无关系,之和属性有关系
interface Animal {
    name: string,
    age: number
}
interface Calculate {
    <T>(a: T,b: T): T   //如果接口约束函数可以用“:”  冒号
}

interface Person {
    name: string,
    age: number,
    speak: () => void   //如果描述的是对象里的属性是函数就不能用冒号就得用“=>”  箭头函数
}

function getName(animal: Animal): string {
    return animal.name
}

let q: Animal = {   
    name: 'zhufeng0',
    age: 10,
}

console.log(getName(q));   //  =>zhufeng0


let p: Person = {   
    name: 'zhufeng1',
    age: 10,
    speak() {}
}

console.log(getName(p));   
// 把p传过来给了getName也行  在ts里和类型无关系,只和属性有关系,因为p里也有name字段,所以p也可(我要的你有就可以,反之我要的你没有不可以)     =>zhufeng1


// 基本类型的兼容性
let num: string | number = 1;
let str: string = 'hello';
num = str;   //赋值

let num2: {
    toString(): string
}
let str2: string = 'jiagou';

num2 = str2;   //因为字符串有toString()属性,所以str2可以赋值给num2

}


namespace b {
// 类的兼容性
//   跟类型无关,只看属性
    class Animal {
        name: string
        // swing: number
    }

    class Bird extends Animal {
        // swing: number
    }
    let a: Animal;
    a = new Bird();   //父类的变量能指向子类的实例

    let b: Bird;
    b = new Animal();   //子类的变量不能指向父类的实例   因为父类没有swing属性

    b = {name: 'zhufeng'}  //不管这个对象的具体类型,只有有这个属性就可

}

namespace c {
    // 函数兼容性
       //1.比较参数
    type sumFunction = (a: number, b: number) => number;
    let sum: sumFunction;
    function f1(a: number, b: number): number{
        return a;
    }
    sum = f1;
    function f2(a: number): number{
        return a;
    }
    sum = f2;
    function f3(a: number, b: number,c: number): number{
        return a;
    }
    // sum = f3;   //报错

    // 综上,可以少参数,不可多参数

      //2.比较返回值

    type GetPerson = () =>{name: string, age: number};
    let getPerson: GetPerson;
    function g1(){
        return {name: 'string', age:10};
    }
    getPerson = g1;
    function g2(){
        return {name: 'string'}
    }
    // getPerson = g2;  //少了可不行


    function g3(){
        return {name: 'string', age:10, home:'beijing'};
    }
    getPerson = g3;  //多了可以

    // 综上,不可少参数,可以多参数


    // 3.函数参数的协变(之前是双向协变,现在是单向的,只能少不能多)  类的定义的兼容性
    type logFunc = (a: number | string) => void;
    let log: logFunc;
    function log1(a: number | string | boolean){
        console.log(a);
    }
    log = log1   //可以多参数,不可以少参数

    // 1和3 的区别: 3是变量的类型   1是变量的数量



    // 4.泛型的兼容性   泛型在判断兼容性的时候先判断具体的类型,然后再进行兼容性判断(关键看属性的兼容性)
    interface Empty<T> {

    }
    let x!: Empty<string>;
    let y!: Empty<number>;
    x = y;  //可以赋值,因为将number传给<T>后变成的是一个{}空对象,将string传给<T>后变成返回的也是一个{}空对象,所以兼容

   
    //  但是
    interface Empty1<T> {
       data: T
    }
    let x1!: Empty1<string>; //{data:string}
    let y2!: Empty1<number>; //{data:number}
    //x1 = y1;  //Error  不可以赋值,因为将number传给<T>后变成的是一个{data:number},将string传给<T>后变成的是一个{data:string},返回的不一样,所以不兼容不能赋值


    // 
    interface Empty1String<T> {
        data: string
     }
     interface Empty1Number<T> {
        data: number
     }

    //  4.枚举的兼容性
        //  枚举类型与数字类型兼容,并且数字类型与枚举类型兼容
        // 不同枚举类型之间是不兼容的
    enum Colors {
        Red,Yellow
    }
    let c:Colors;
    c = Colors.Red; //=0
    c = 1;

    let d: number;
    d = Colors.Yellow;  //1
}

10. 类型保护

// 类型保护  就是更精确的看是哪种类型
namespace a{
    // 1.typeof保护
function double(input: string | number | boolean){
    if(typeof input == 'string'){
        input.toLowerCase();
    }else if(typeof input == 'number'){
        input.toFixed(2);
    }else{
       input
    }
 }
    // 2.instanceof保护
    class Animal{
        public name: string = 'zhufeng'
    }
    class Bird extends Animal{
        public swing: number = 2
    }
    function getName(a: Animal){
        if(a instanceof Bird){   //a是Bird的实例
            a.swing;
        }else{
            a.name
        }
    }
 
 //   3.null保护
 function func(s: string | null){
 //    if(s == null){ s = ''}
    //    s = s || ''
 
     // function en(){
     //     s = s || ''
     // }
     // en();
 
    return s!.charAt(0);   //非空断言
 }
 
 // 4.链判断运算符
 /* a?.b //如果a是null或undefined,那么返回undefined,否则返回a.b的值
 a == null ? undefined : a.b
 
 a?.[x]  //如果a是null或undefined,那么返回undefined,否则返回a.[x]的值
 a == null ? undefined : a[x]
 
 a?.b()  //如果a是null或undefined,那么返回undefined,否则返回a.b()的值
 a == null ? undefined : a.b()
 
 a?.()  //如果a是null或undefined,那么返回undefined,否则返回a.()的值
 a == null ? undefined : a.() */
 
 
 
 
 // 5.可辨识的联合类型(怎么区分)
     //利用联合类型中的共有字段进行类型保护的一种技巧
     //相同字段的不同取值就是可辨识
 
 // 相同属性不同取值来判断
 interface WarningButton {
     calss: 'warning',   //字面量类型,可以写死值
     text1: '修改'
 }
 interface DangerButton {
     calss: 'danger',
     text2: '错误'
 }
 type Button = WarningButton | DangerButton
 function getButton(button: Button){
     if(button.calss === 'warning'){
         button.text1
     }else{
         button.text2
     }
 }
 
 // 不同字段属性
 interface Bird{
     swing: number
 }
 
 interface Dog{
     leg: number
 }
 function getNumber(x: Bird | Dog){
   if('swing' in x){
     x.swing;
   }else{
     x.leg
   }
 }
 
}

// 自定义的类型保护
namespace b{
    interface Bird{
        name1: 'Bird'
        legs: number
    }
    
    interface Dog{
        name2: 'Dog'
        legs: number
    }
    function isBird(x: Bird | Dog): x is Bird{
        return x.legs === 2;   //返回了一个布尔值


    }
    function getAnimal(x: Bird | Dog){
        if(isBird(x)){   //判断isBird(x)是否为true
            x.name1; //x就是鸟
            console.log('ppp',x.name1);
            
        }else{
            x.name2;
            console.log(x.name2);
            
        }
      
    }

    let x: Bird = {name1:'Bird',legs:2}
    getAnimal(x)
}

11. 交叉类型

// 交叉类型  就是;两个接口类型的属性的并集

namespace a{
    interface Bird{
        name: string
        fly():void
    }
    
    interface Person{
        name: string
        eat():void
    }
    type BirdMan = Bird & Person;
    let p: BirdMan = {
        name: 'Dog',
        eat(){},
        fly(){}
    }
}


namespace b{
    // typeof  可以获取变量的类型
/*     type Person = {
        name: string;
        age: number
    } */
  /*   let p: Person = {
        name: 'zhufeng',
        age: 10
    } */
    let p = {
        name: 'zhufeng',
        age: 10
    }   //1.先定义值
    type Person = typeof p;  //2.通过typeof拿到值的类型,再修饰变量
    let p2: Person = {
        name:'zhufeng',
        age:20
    }



    // 索引访问操作符,我们可以通过[]来获取一个类型的子类型
    interface Person2 {
        name: string;
        age: number;
        job: {
            name: string
        },
        interests: {name: string; level: number}[]
        // interests: Array<{name: string; level: number}>
    }
    let myname: Person2['job']['name'] = 'fe';
    let mylevel: Person2['interests'][0]['level'] = 10



    // keyof   索引类型查询操作符
    interface Person3{
        name: string
        age: number,
        gender: 'mail' | 'female',
        //[propName: string]: any  //可以接受任意属性,但是不能这样写
    }
    // type Person3Key = 'name' | 'age' | 'gender'
    type Person3Key = keyof Person3   //返回一个接口所有key的一个集合
    function getValueByKay(val: Person3, key: Person3Key){
        return val[key]
    }
    let person3: Person3 = {
        name: 'zhufeng',
        age: 10,
        gender: 'mail' ,
        //[propName: string]: any  //可以接受任意属性,但是不能这样写
    }
    let name = getValueByKay(person3,'name')
    console.log(name);


    // 映射类型   在定义时用in操作符批量定义,基于一个老的类型定义一个新的类型(由非可选变成可选)
    interface Person4{
        name: string
        age: number,
        gender: 'mail' | 'female',
    }
    type PartialPerson = {
        [key in keyof Person4] ?: Person4[key]   //key in => for in
    }
    let p4: PartialPerson = {
        name: 'zhufeng',
        age: 10,
    }
    console.log(p4);
    

   /*  原理:
     type  Partial<T> = {
        [key in keyof T]?: T[key]
    } */
    // 就是以下意思  ?是可选的,可有可无
  /*   type PartialPerson = {
        name?: string
        age?: number,
        gender?: 'mail' | 'female',
    } */



    // 反过来  由可选变为必选
    type RequirePerson = Required<Person4>;
    let p5: RequirePerson = {
        name: 'zhufeng',
        age: 10,
        gender: 'mail',
    }
    /*  //原理:
     type  Partial<T> = {
        [key in keyof T]-?: T[key]
    } */

    // 只读
    type ReadonlyPerson = Readonly<Person4>;
    let p6: ReadonlyPerson = {
        name: 'zhufeng',
        age: 10,
        gender: 'mail',
    }
    // p6.name = 'jiagou'  //无法分配到 "name" ,因为它是只读属性。

    /*  //原理:
     type  Readonly1<T> = {
        readonly [key in keyof T]-?: T[key]
    } */

    // Pick  捡几个子类型
    type PickPerson = Pick<Person4,'name'>  //提取Person4接口中的name类型
    // 上面哪一行相当于下面这个
    /* interface PickPerson {
        name:'zhufeng'
    } */
    let x: PickPerson = {
        name:'name'
    }
    /*  //原理:
    type Pick<T, K extends keyof T> = {
        [key in K]: T[key]
    } */

    // TS  要区分类型和值  类型:interface class enum  值:let function var const
    // interface Person4 = Person4   //interface不能当做值来用



    

}
namespace c{
    // typeof  和  type  的区别

    // type是类型   类似number  string
    let x = {
        name:'name'
    }
    let a = typeof x;   //赋值为  object  这是js代码
    type b = typeof x;     //这是ts专有的  编译后就没了

    console.log(a);
    console.log(b);
    
    // 元祖的长度和类型是固定的
    let xx: [string, number] = ['1',2]
    console.log(xx[1 ]);


    // 什么时候用interface  type  class
    // interface 是定义接口类型,他是真实的类型,也可能会被导出和导入   这是开创一个新的类型   
    // type只是临时用的别名,不会产出真正的类型   基于老类型定义新类型 type是基于别人进行加工
    // class就是定义类 new xxx

    
    
}

12. 命名空间

namespace a{
    interface Fish{
        name1: string
    }
    interface Fish2{
        name1: string,
        age: number
    }
    interface Water{
        name2: string
    }
    interface Bird{
        name3: string
    }
    interface Sky{
        name4: string
    }
    type Condition<T> = T extends Fish ? Water : Sky;
    let condition: Condition<Fish2> = {  //判断继承:看属性有无
        name2: 'water'
    }

    // 条件类型的分发
    type Condition2<T> = T extends Fish ? Water : Sky;   //通过T传过来的东西看里面有没有Fish里的属性,如果有就是true,没有就是false
    let c1: Condition2<Fish | Bird> = {name2:'zhufeng'}
    let c2: Condition2<Fish | Bird> = {name4:'zhufeng'}
    let c3: Water | Sky = {name2:'zhufeng'}
    let c4: Water | Sky  = {name4:'zhufeng'}
}


namespace c{
    // 关键字
    type E = Exclude<string | number, string>; //从string | number, string 中把string排除掉,从前者排除后者
    let e: E = 10;

    type E2 = Extract<string | number | null, string>  //从<string | number | null提取 string
    let e2: E2 = 'hello';

    type E3 = NonNullable<string | number | null | undefined>;  //把不为空的干掉
    let e3: E3 = 'hello';
    let e4: E3 = 10;

    // redux会用到Returntype  通过这个判断返回值类型
    function getUserInfo(){
        return {name:'zhufeng', age:10}
    }
    type UserInfo = ReturnType<typeof getUserInfo>
    let user: UserInfo =  {
        name:'zhufeng',
        age:10
    }

    // instanceType  获取构造函数的实例类型
    class Person5{
        name: string;
        constructor(name: string){
            this.name = name;
        }
    }
    type P = InstanceType<typeof Person5>;
    let p: P = new Person5('zhufeng')

    // 导入导出
    // 命名空间:1.封装类似的代码   2.防止命名冲突
}

13. 声明文件

// 声明文件怎么写
/*
  用ts重写一遍
  配上声明文件
*/


/* declare const $:(selector: string)=>{
    click(): void;
    width(length: number): void
} */
$('#name').click()

declare let name1:string;
declare let age: number;
declare function getName() :void;
declare class Animal {name: string}

interface Person6{
    name: string
}
type Student = Person6 | string;
declare const enum Seasons{
  Spring,
  Summer,
  Autumn,
  Winter
}
let siji: Seasons[] = [
    Seasons.Spring,
    Seasons.Summer,
    Seasons.Autumn,
    Seasons.Winter,
]
console.log(siji);


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值