typescrip基础

175 阅读11分钟

1.基础类型
juejin.cn/post/684490…

1.typescript一共有多少种类型,新增了哪几个类型
    布尔,数字,字符串,数组,Object
    undefined,Null:null,undefined是所有类型的子类型,null和undefined可赋值给number类型的变量。
    元组:let x: [string, number];
    枚举:enum Color {
            Red = 1, 
            Green = 2, 
            Blue = 4
          }
    Any
    Void:void类型没什么用,只能赋undefined和null
    never
注意:在写类型的时候,只有Object和Array是开头大写的,其余的都是开头小写
2.数组的两种声明方式
3.元组和数组的不同之处(2点)
4.枚举:编号,手动修改编号,映射

2.接口

interface LabelledValue {
  label: string;
  color?: string;
  readonly x: number;
  [propName: string]: any;
}
1.属性只可以多但不能少
2.可选属性
3.只读属性
4.什么是额外属性检查:对象字面量会经过额外属性检查,当将它们赋值给变量或作为参数传递的时候。 如果一个对象字面量存在任何“目标类型”(这里的任何目标指的是可选属性,必须有的属性,可能存在的属性)不包含的属性时,你会得到一个错误。
5.处理额外属性的三个方法
    使用类型断言:createSquare({ width: 100, opacity: 0.5 } as SquareConfig);
    添加额外属性
6.可索引的类型:只要有【】括号,且后面的类型不是any,是number也罢,还是联合类型也罢,都是
  可索引的类型
    interface LabelledValue {
      [propName: string]: string;
      [index: string]: number;
      [age: string]: boolean;
      [width: string]: number;
      [height: string]: number;
-----------------上面是----------------------------------
      [propName: string]: any;
      [index: string]: any;
      [age: string]: any;
      [width: string]: any;
      [height: string]: any;
    }
  可索引的类型和和额外属性检查写法上很像,但是有唯一的区别,就是对值的约束类型不一样,
  额外属性检查必须是any
7.接口函数类型:
    声明
    interface SearchFunc {
      (source: string, subString: string): boolean;
    }
    约束变量
    let mySearch: SearchFunc;
    类型约束:
        参数:
            变量名可以随意,但是(参数)对应位置的类型必须一致
            返回值的类型必须要和接口约束的类型一致
    mySearch = function(src: string, sub: string): boolean {
      let result = src.search(sub);
      return result > -1;
    }
    
8.类类型接口:
        类静态部分与实例部分的区别:
        
        // 通过一个函数,作为传参约束实例部分
        interface ClockConstructor {
            new (hour: number, minute: number): ClockInterface;
        }
        // class实现这个接口,用于约束静态部分
        interface ClockInterface {
            tick();
        }
        // 必须通过一个函数来约束,这个函数具有一下特点:
               传入的参数:实例部分约束,传入类和实例化用的参数
               返回的约束:静态部分约束,
function createClock(ctor: ClockConstructor, hour: number, minute: number): ClockInterface {
    return new ctor(hour, minute);
}
        
        class DigitalClock implements ClockInterface {
            constructor(h: number, m: number) { }
            tick() {
                console.log("beep beep");
            }
        }
        class AnalogClock implements ClockInterface {
            constructor(h: number, m: number) { }
            tick() {
                console.log("tick tock");
            }
        }
        
        let digital = createClock(DigitalClock, 12, 17);
        let analog = createClock(AnalogClock, 7, 32);

3.类型兼容性

类型兼容性主要解决引用类型赋值的时候的类型检测问题
1.对象赋值:赋值者属性可多不可少,多余属性不检测
interface Named {
    name: string;
}
let x: Named;
let y = { name: 'Alice', location: 'Seattle' }; // 多一个location,不检测
x = y; // OK
2.函数赋值:参数名称不管,类型一致,赋值者参数可少不能多
let x = (a: number) => 0;
let y = (b: number, s: string) => 0;
y = x; // OK
x = y; // Error 参数不能多
3.可选参数及剩余参数
可选参数与必须参数是可互换的。 源类型上有额外的可选参数不是错误,目标类型的可选参数在源类型里没有对应的参数也不是错误。
4.类的兼容性:符合对象的兼容性,直观实例属性
class Animal {
//实例属性number必须检测
    feet: number;
// 静态属性name不用检测
    static name:string;
// 构造属性也不用检测
    constructor(name: string, numFeet: number) { }
}
class Size {
    feet: number;
    constructor(numFeet: number) { }
}
let a: Animal;
let s: Size;
// 因为实例属性一致的
a = s;  // OK
s = a;  // OK
类的私有成员和受保护成员
当检查类实例的兼容时,如果目标类型包含一个私有成员,那么源类型必须包含来自同一个类的这个私
有成员。 同样地,这条规则也适用于包含受保护成员实例的类型检查。 这允许子类赋值给父类,但是不能赋值给其它有同样类型的类。

5.泛型:对于没指定泛型类型的泛型参数时,会把所有泛型参数当成any比较。 然后用结果类型进行比较
检测两点:key一致  value的类型一致

// key和value的类型都一致,value类型没有指定,默认是any
interface Empty<T> {
}
let x: Empty<number>;
let y: Empty<string>;
x = y;  //ok  
// key一致,但是vlue类型不一致,不兼容
interface NotEmpty<T> {
    data: T;
}
let x: NotEmpty<number>;
let y: NotEmpty<string>;
x = y;  // Error

对于函数检测:
    

3.高级类型

1.交叉类型

2.联合类型:联合类型约束变量  和  联合接口约束对象是不一样的
    联合类型约束变量:并集
    function padLeft(value: string, padding: string | number) {
        // padding 符合 string 或 number
    }
    联合接口约束对象:交集
    interface Bird {
        fly();
        layEggs();
    }
    interface Fish {
        swim();
        layEggs();
    }
    function getSmallPet(): Fish | Bird {
        // 返回的对象只能使用Fish 和 Bird的共有属性
    }
    let pet = getSmallPet();
    pet.layEggs(); // okay
    pet.swim();    // err  非共有的属性
3.类型保护与区分类型
    3.1 类型断言:<Fish>pet  方括号断言类型
            let pet = getSmallPet();
            // 每一个成员访问都会报错
            if (pet.swim) {
                pet.swim();
            }
            else if (pet.fly) {
                pet.fly();
            }
            添加类型断言就OK了
            let pet = getSmallPet();
            if ((<Fish>pet).swim) {
                (<Fish>pet).swim();
            }
            else {
                (<Bird>pet).fly();
            }
    3.2 typeof类型保护
        typeof类型保护*只有两种形式能被识别:
            typeof v === "typename"
            typeof v !== "typename""typename"必须是 "number""string""boolean""symbol"。
    3.3 instanceof的右侧要求是一个构造函数,TypeScript将细化为:
        if (padder instanceof StringPadder) {
            padder; // 类型细化为'StringPadder'
        }
    3.4可选参数:可选参数会被自动地加上 | undefined:也就是只能不赋值或者赋值undefiend或者赋值对应的类型
        function f(x: number, y?: number) {
            return x + (y || 0);
        }
        f(1, 2);
        f(1);
        f(1, undefined); // Ok
        f(1, null); // err 只能赋值undefiend
    3.5可选属性:只能不赋值或者赋值undefiend或者赋值对应的类型
        class C {
            a: number;
            b?: number;
        }
        let c = new C();
        c.b = 13;
        c.b = undefined; // ok
        c.b = null; // error, 'null' is not assign
        
    3.6如果编译器不能够去除 null或 undefined,你可以使用类型断言手动去除。 语法是添加 !后缀:
        function fixed(name: string | null): string {
           // !后缀去除了null和undefined
           return name!.charAt(0)
        }
    3.7类型别名 type 可以像接口那样约束变量
        type Name = string;
        type NameResolver = () => string;
        联合类型配合类型别名比较多
        type NameOrResolver = Name | NameResolver;
        function getName(n: NameOrResolver): Name {
            if (typeof n === 'string') {
                return n;
            } else {
                return n();
            }
        }
        类型别名和接口的区别:
            1.类型别名不能被 extends和 implements
            2.需要使用联合类型或元组类型,这时通常会使用类型别名。
            type strOrNum = string | number
    3.8字符串字面量类型:允许你指定字符串必须的固定值
        3.8.1类型别名 联合类型 类型保护配合使用,实现类似枚举的功能
            type Easing = "ease-in" | "ease-out" | "ease-in-out";
            class UIElement {
                animate(dx: number, dy: number, easing: Easing) {
                    if (easing === "ease-in") {
            
                    }
                }
            }
        3.8.2允许你指定字符串必须的固定值可以区分函数重载:
            // 固定传入img
            function createElement(tagName: "img"): HTMLImageElement;
            // 固定传入input
            function createElement(tagName: "input"): HTMLInputElement;

    
    














10.5 映射类型在定义的时候用in操作符去批量定义类型中的属性
    interface Person{
      name:string;
      age:number;
      gender:'male'|'female';
    }
    //批量把一个接口中的属性都变成可选的
    type PartPerson = {
      [Key in keyof Person]?:Person[Key]
    }
    let p1:PartPerson={};
    //也可以使用泛型
    type Part<T> = {
      [key in keyof T]?:T[key]
    }
    let p2:Part<Person>={};
10.6 内置工具类型TS 中内置了一些工具类型来帮助我们更好地使用类型系统
10.6.1 PartialPartial 可以将传入的属性由非可选变为可选,具体使用如下:
    type Partial<T> = { [P in keyof T]?: T[P] };
    interface A {
      a1: string;
      a2: number;
      a3: boolean;
    }
    type aPartial = Partial<A>;
    const a: aPartial = {}; // 不会报错
10.6.2 RequiredRequired 可以将传入的属性中的可选项变为必选项,这里用了 
-? 修饰符来实现。
    //type Required<T> = { [P in keyof T]-?: T[P] };
    interface Person{
      name:string;
      age:number;
      gender?:'male'|'female';
    }
    /**
     * type Require<T> = { [P in keyof T]-?: T[P] };
     */
    let p:Required<Person> = {
      name:'zhufeng',
      age:10,
      //gender:'male'
    }
10.6.3 ReadonlyReadonly 通过为传入的属性每一项都加上 readonly 修饰符来实现。
    interface Person{
      name:string;
      age:number;
      gender?:'male'|'female';
    }
    //type Readonly<T> = { readonly [P in keyof T]: T[P] };
    let p:Readonly<Person> = {
      name:'zhufeng',
      age:10,
      gender:'male'
    }
    p.age = 11;
10.6.4 PickPick 能够帮助我们从传入的属性中摘取某一项返回
    interface Animal {
      name: string;
      age: number;
    }
    /**
     * From T pick a set of properties K
     * type Pick<T, K extends keyof T> = { [P in K]: T[P] };
     */
    // 摘取 Animal 中的 name 属性
    type AnimalSub = Pick<Animal, "name">; //{ name: string; }
    let a:AnimalSub = {
        name:'zhufeng',
        age:10
    }
10.6.5 映射类型修饰符的控制TypeScript中增加了对映射类型修饰符的控制具体而言,一个 readonly 或 ? 修饰符在一个映射类型里可以用前缀 + 或-来表示这个修饰符应该被添加或移除TS 中部分内置工具类型就利用了这个特性(Partial、Required、Readonly...),这里我们可以参考 Partial、Required 的实现
10.7 条件类型在定义泛型的时候能够添加进逻辑分支,以后泛型更加灵活
    10.7.1 定义条件类型
        interface Fish {
            name: string
        }
        interface Water {
            name: string
        }
        interface Bird {
            name: string
        }
        interface Sky {
            name: string
        }
        //三元运算符
        type Condition<T> = T extends Fish ? Water : Sky;
        let condition: Condition<Fish> = { name: '水' };
    10.7.2 条件类型的分发
        interface Fish {
            fish: string
        }
        interface Water {
            water: string
        }
        interface Bird {
            bird: string
        }
        interface Sky {
            sky: string
        }
        type Condition<T> = T extends Fish ? Water : Sky;
        //(Fish extends Fish ? Water : Sky) | (Bird extends Fish ? Water : Sky)
        // Water|Sky
        let condition1: Condition<Fish | Bird> = { water: '水' };
        let condition2: Condition<Fish | Bird> = { sky: '天空' };
10.7.3 内置条件类型TS 在内置了一些常用的条件类型,可以在 lib.es5.d.ts 中查看:
    这个都只针对联合类型做排除和提取,联合类型中的元素可以是interface也可以是基础类型
    10.7.3.1 Exclude从 T 可分配给的类型中排除 U
        // 这个的意思就是从string|number中排除string,两个参数,排除后面的参数
        type  E = Exclude<string|number,string>;
        let e:E = 10;
    10.7.3.2
        Extract从 T 可分配的类型中提取 U
        // 这个的意思就是从string|number中只能用string,两个参数,只能用后面的参数
        type  E = Extract<string|number,string>;
        let e:E = '1';
    10.7.3.3
        NonNullable从 T 中排除 null 和 undefined,只有一个参数就是联合类型,排除的是固定的
        只能排除 null 和 undefined,是默认的
        type  E = NonNullable<string|number|null|undefined>;
        let e:E = null;
    10.7.3.4
        ReturnType获取函数类型的返回类型 `
        function getUserInfo() { 
            return { name: "zhufeng", age: 10 }; 
        }
        // 通过 ReturnType 将 getUserInfo 的返回值类型赋给了 UserInfo 
        // 切记这里是ReturnType;不是getUserInfo
        type UserInfo = ReturnType;
        const userA: UserInfo = { name: "zhufeng", age: 10 };
    10.7.3.5 InstanceType<T>
        获取构造函数类型的实例类型,name和getName属性必须有,其实约束的就是属性,
        这个例子中属性有哪些呢:name, getName两个,约束的就是这两个属性
        
            class Person{
              name:string;
              constructor(name){
                this.name = name;
              }
              getName(){console.log(this.name)}
            }
            type  P = InstanceType<typeof Person>;
            let p:P = {name:'zhufeng',getName(){}};
    11.类型声明声明文件可以让我们不需要将JS重构为TS,只需要加上声明文件就可以使用系统类型声明在编译的时候都会被删除,不会影响真正的代码
        11.1 普通类型声明
            declare const $:(selector:string)=>{ 
                //变量
                click():void;
                width(length:number):void;
            };
            declare let name:string;  //变量
            declare let age:number;  //变量
            declare function getName():string;  //方法
            declare class Animal{name:string}  //类
            interface Person{ //声明接口
                name:string
            }
            type Student = { //声明类型
                name:string
            }|'string';
    11.2 外部枚举外部枚举是使用
        declare 只能用在第三方类型声明中,.d.ts中,因为在编译的时候会删除,所以只能做类型定义,不能做项目中要用的代码的变量声明,否则编译后代码中会找不到这个变量
        declare enum定义的枚举类型外部枚举用来描述已经存在的枚举类型的形状
            declare enum Seasons {
                Spring,
                Summer,
                Autumn,
                Winter
            }
            let seasons = [
                Seasons.Spring,
                Seasons.Summer,
                Seasons.Autumn,
                Seasons.Winter
            ];
        declare 定义的类型只会用于编译时的检查,编译结果中会被删除。上例的编译结果如下
            var seasons = [
                Seasons.Spring,
                Seasons.Summer,
                Seasons.Autumn,
                Seasons.Winter
            ];
        也可以同时使用
            declare 和 constdeclare const enum Seasons {
                Spring,
                Summer,
                Autumn,
                Winter
            }

            let seasons = [
                Seasons.Spring,
                Seasons.Summer,
                Seasons.Autumn,
                Seasons.Winter
            ];
        编译结果
            var seasons = [
                0 /* Spring */,
                1 /* Summer */,
                2 /* Autumn */,
                3 /* Winter */
            ];
            
10.2 typeof可以获取一个变量的类型//先定义类型,再定义变量
        type People = {
            name:string,
            age:number,
            gender:string
        }
        let p1:People = {
            name:'zhufeng',
            age:10,
            gender:'male'
        }
        //先定义变量,再定义类型
        //用在先不知道p1的类型的情况下,通过type of获取到p1的类型约束,在赋值给People,
          在通过People去约束其他的变量
        let p1 = {
            name:'zhufeng',
            age:10,
            gender:'male'
        }
        type People = typeof p1;
        function getName(p:People):string{
            return p.name;
        }
        getName(p1);