TypeScript-基础篇

196 阅读18分钟

类型基础

强类型

不允许改变变量的数据类型,除非进行强制类型转换 (Java,C#,Python,C/C++)

弱类型

变量可以被赋予不同的数据类型 (PHP,JavaScript)

动态类型

在执行阶段确定所有变量的类型 (Python)

静态类型

在编译阶段确定所有变量的类型 (Java,C/C++,C#)

基础类型

  • number
  • string
  • boolean
  • Array
  • Tuple
  • function
  • object
  • symbol (独一无二的值)
  • void (没有类型)
  • any (任意类型)
  • undefined (未定义)
  • null (没有值)
  • never (永远不存在的值) (1.抛异常 2.死循环)

枚举类型

枚举:一组具有名字的常量集合

数字枚举

  • 枚举成员值默认从0递增,也可自定义,支持反向映射
    // 数字枚举
    enum Role{
        Reporter,
        Developer,
        Maintainer,
        Owner,
        Guest
    }
    console.log(Role.Reporter) // 0
    

字符串枚举

  • 不支持反向映射
    // 字符串枚举
    enum Message {
        Success = '成功',
        Error = '失败',
    }
    console.log(Message);
    

异构枚举

  • 字符串枚举和数字枚举混用
    // 异构枚举
    enum Anser{
        N,
        Y='yes'
    }
    

枚举成员

  • 拥有只读属性
  • 1.const member(常量枚举,在编译阶段被计算出结果)
    • 1.无初始值
    • 2.对常量成员的引用
    • 3.常量表达式
  • 2.computed member(计算枚举,表达式保留到程序的执行阶段)
    • 非常量表达式
    enum Char {
        // 常量枚举
        a, // 无初始值
        b = Char.a, // 对常量成员的引用
        c = 1 + 3, //常量表达式// 
        // computed member 执行阶段(运行时) (computed member 后面的成员需要赋予初始值)
        d = Math.random(), // (执行阶段,才会计算)
        e = '123'.length, // (执行阶段,才会计算)
        f = 4, //需要赋予初始值
    }
    

常量枚举

使用const的声明的枚举就是常量枚举

  • 编译后被移除
  • 成员只能为 const member(常量枚举)
     const enum Month {
         Jan,
         Feb,
         Mar,
     }
     let mounth = [Month.Jan, Month.Feb, Month.Mar];
     console.log(mounth); // [0,1,2]
    

枚举/枚举成员作为类型

  • 1.无初始值
  • 2.枚举成员均为数字
  • 3.枚举成员均为字符串
    enum E {a,b,} //无初始值
    enum F {a = 0,b = 1,} //均为数字
    enum G {a = 'apple', b = 'banana',} //均为字符串
    

接口

对象类型接口

// 对象类型接口
interface List {
    readonly id: number;// 只读属性
    name: string;
}
interface Result {
    data: List[];
}
function render(result: Result) {
    result.data.forEach(item => {
        console.log(item); 
    });
}
// 将对象字面量赋值给变量
let result = {
    data: [
        {id: 1, name: 'a', sex: 'male',}, // 鸭式辩型法
        {id: 2, name: 'b', age: 10,},
    ],
};
render(result);
  • 检查原则:鸭式辩型法 (动态语言风格)
    • 绕过对象字面量检查

      • 1.将对象字面量赋值给变量
      • 2.使用类型断言
         // 类型断言一 (推荐使用这种)
         render({
             data: [
                 { id: 1, name: 'a', sex: 'male' },
                 { id: 2, name: 'b' },
             ],
         } as Result);
         // 类型断言二( 在react会引起歧义)
         render(<Result>{
             data: [
                 { id: 1, name: 'a', sex: 'male' },
                 { id: 2, name: 'b' },
             ],
         });
      
      • 3.使用字符串索引签名
      interface List {
          readonly id: number;
          name: string;
          [x: string]: any; //字符串索引签名  用任意的字符串去索引List,可以得到任意的结果,这样List就支持多个属性
      }
      
    • 对象的属性

      • 1.可选属性
      • 2.自读属性
      interface List {
          readonly id: number;// 只读属性
          name: string;
          age?: number; //可选属性 ?表示可有可无
      }
      

可索引类型接口

  • 数字索引
    // 数字索引
    interface StringArray {
        [index: number]: string; //用任意的数字索引StringArray,都会得到string(相当于声明了一个字符串类型的数组)
    }
    let chat: StringArray = ['A', 'B'];
    console.log(chat); //["A", "B"]
    
  • 字符串索引
    // 字符串索引接口
    interface Names {
        [x: string]: string; //用任意的字符串去索引Names,都会得到string
        // y: number; //不被允许
        [z: number]: string; //用任意的数字索引Names,都会得到string
    }
    let str: Names = { a: 'c', 2: 'b'  };
    console.log(str);//{2: "b", a: "c"}
    

函数类型接口

interface H {
    (arg: type): type;
}
// 类型别名 使用type
type Add = (x: number, y: number) => number; // 跟上面二者等价
let add:Add=(a,b)=>a+b;

混合类型接口

一个接口既可以定义一个函数,也可以像对象一样,有属性和方法

interface H {
    (arg:type): type; //定义一个函数
    prop: type;//定义属性
    method(arg:type): type;//定义方法
}
// 示例
interface Lib {
    (): void;
    version: string;
    doSomething(): void;
}
function getLib() {
    let lib: Lib = (() => {}) as Lib; // 类型断言
    lib.version = '1.0';
    lib.doSomething = () => {};
    return lib;
}
let lib1 = getLib();
let lib2 = getLib();
console.log(lib1 === lib2);// false

类类型接口

  • 类必须实现接口中的所有属性
  • 接口只能约束类的公有成员,不能约束私有成员,受保护成员,静态成员和构造函数
// 接口
interface Human {
    // new (name: string): void; // 接口不能约束构造函数
    name: string;// 接口只能约束类的公有成员
    eat(): void;
}
// 类继承接口
// 类实现接口的时候,必须实现接口中声明的所有属性(接口属性为公用成员)
class Asian implements Human {
    constructor(name: string) {
        this.name = name;
    }
    name: string;
    eat() {}
    sleep() {}// 可以定义自有方法
}

接口继承接口

  • 抽离可复用的接口
  • 将多个接口整合成一个接口
interface Human {
    name: string;
    eat(): void;
}
// 继承Human
interface Man extends Human {
    run(): void;
}
// 单独定义Child接口
interface Child {
    cry(): void;
}

// Boy接口继承多个接口
interface Boy extends Man, Child {}

// 定义一个对象,必须需要包含四个属性,分别来自Human,Man, Child
let boy: Boy = {
    name: '',
    run() {},
    eat() {},
    cry() {},
};

接口继承类

  • 抽象出类的公有成员,私有成员和受保护成员 成员结构
// 类
class Auto {
    state = 1; // 公共属性
}
// 当接口继承了一个类类型时,它会继承类的成员结构但不包括其实现
// 接口继承类
interface AutoInterface extends Auto {
    // AutoInterface接口隐含了state属性
    select(): void;
}
// 类继承接口 
class C implements AutoInterface {
    state = 2;
}
// 子类
// 当一个接口继承了一个拥有私有或受保护的成员的类时,只能被这个类或其子类所继承
class Bus extends Auto implements AutoInterface {
    select() {}
}

函数

定义函数

  • 定义方式

    定义函数类型,无函数体

    1. function
    2. 变量定义
    3. 类型别名
    4. 接口定义
    // 函数定义
    function add1(x: number, y: number) {
        return x + y;
    }
    //变量定义
    let add2: (x: number, y: number) => number;
    // 类型别名
    type add3 = (x: number, y: number) => number;
    // 接口定义
    interface add4 {
        (x: number, y: number): number;
    }
    
  • 类型要求
    • 参数类型必须声明
    • 返回值类型一般无需声明

函数参数

  • 参数个数
    • 实参和形参必须一一对应
  • 可选参数
    • 必选参数不能位于可选参数后面
    // 可选参数 必须位于必选参数之后
    function add5(x: number, y?: number) {
        return y ? x + y : x;
    }
    
  • 默认参数
    • 在必须参数前,默认参数不可省略(可使用undefined,来获取默认参数值)
    // 默认值
    function add6(x: number, y = 2, z: number, q = 1) {
        console.log(y); // 2
        return x + y + z + q; //1+2+3+1=7
    }
    console.log(add6(1, undefined, 3));//可使用undefined,来获取默认参数值
    
    • 在必须参数后,默认可以省略
  • 剩余参数
function add7(x: number, ...rest: number[]) {
    return x + rest.reduce((pre, cur) => pre + cur);
}
console.log(add7(1, 2, 3, 4, 5));//15

函数重载

静态语言:函数的名称相同,参数的个数或类型不同 TypeScript:预先定义一组名称相同,类型不同的函数声明,并在一个类型最宽泛的版本中实现重载

// 先定义类型不同的函数声明
function add8(...rest: number[]): number;// 把最容易匹配的写在最前面
function add8(...rest: string[]): string;
// 在一个类型最宽泛的版本中实现重载
function add8(...rest: any[]): any {
    let first = rest[0];
    if (typeof first === 'string') {
        return rest.join('');
    } else if (typeof first === 'number') {
        return rest.reduce((pre, cur) => pre + cur);
    }
}
console.log(add8(1, 2, 3)); //6
console.log(add8('a', 'b', 'c')); //abc

基本实现

类中定义的属性都是实例属性,类中定义的方法都是原型方法 实例属性必须有初始值,或在构造函数中被赋值,或为可选成员

class Dog {
    // 构造参数添加类型注解
    constructor(name: string) {
        this.name = name; // 实例属性必须有初始值,或在构造函数中被赋值
    }
    name: string; //实例属性 (成员属性 添加类型注解)
    run() {} // 原型方法
}

继承

子类的构造函数中必须包含super调用

// 类的继承
class Husky extends Dog {
    constructor(name: string, public color: string) {
        super(name); // super表示父类的实例
        this.color = color;
    }
}

成员修饰符

  • public
    • 对所有人可见,所有成员默认为public
  • private
    • 只能在被定义的类中访问,不能通过实例或子类访问
    • private constructor:不能被实例化,不能被继承
  • protected
    • 只能在被定义类和子类访问,不能通过实例访问
    • protected constructor:不能被实例化,只能被继承,相当于声明了一个基类
  • readonly
    • 必须有初始值,或者构造函数中被赋值
  • static
    • 只能有类名调用,不能通过实例访问,可继承
class Dog {
    // private constructor:不能被实例化,不能被继承
    // protected constructor:不能被实例化,只能被继承,相当于声明了一个基类
    constructor(name: string) {
        this.name = name;
    }
    //显式声明
    public name: string = 'dog'; // 公有成员(实例属性)
    private pri() {   // 私有成员 可被类访问 不可被子类和实例调用
        console.log('pri');
    }
    protected pro() { // 受保护成员 可以在类和子类访问,但不可在实例中访问
        console.log('pro');
    }
    readonly legs: number = 4;//只读属性 不能被更改,一定要被初始化
    run() {}
    // 静态成员 只能通过类名来调用,不可通过实例来调用.可被子类继承和调用
    static food: string = 'bones';
}

构造函数参数中的修饰符

  • 将参数变为实例属性
    class Dog3 {
        // 将参数变为实例属性
        constructor(public name: string) {
            this.name = name;
        }
    }
    

抽象类

  • 不能被实例化,只能被继承
    • 抽象方法包含具体实现
      • 子类直接复用
    • 抽象方法不包含具体实现
      • 子类必须实现
// 抽象类
abstract class Animal {
    // 抽象方法包含具体实现
    eat() {
        console.log('eat');
    }
    // 抽象方法只能出现在抽象类中,抽象方法不包含具体实现
    abstract sleep(): void; // 方法签名必须在派生类中实现
}
// 无法创建抽象类的实例
// let animal = new Animal();
class Dog extends Animal {
    constructor(name: string) {
        super();
        this.name = name;
    }
    name:string
    run() {}
    sleep() {
        console.log('dog sleep');
    }
}
let dog = new Dog('wangwang');
dog.eat();// eat 子类直接复用
  • 多态
    • 多个子类对父抽象类的方法有不同实现,实现运行时绑定
class Cat extends Animal {
    constructor() {
        super();
    }
    // 多个子类对父抽象类的方法有不同实现,实现运行时绑定
    sleep() {
        console.log('cat sleep');
    }
}
let cat = new Cat();
cat.sleep(); // cat sleep

this类型

  • 实现实例方法的链式调用
  • 在继承时,具有多态性,保持父子类之间接口调用的连贯性
// 方法的链式调用
class WorkFlow {
    step1() {
        return this;
    }
    step2() {
        return this;
    }
}
// 实例方法的链式调用
new WorkFlow().step1().step2();

class MyFlow extends WorkFlow {
    next() {
        return this;
    }
}
//在继承时,具有多态性,保持父子类之间接口调用的连贯性
new MyFlow().next().step1().next().step2();

泛型

支持多种类型的方法

  • 函数重载
    function log(value:string):string;
    function log(value:string[]):string[];
    function log(value: any) {
        console.log(value);
        return value;
    }
    
  • 联合类型
    function log(value: string|string[]):string|string[] {
        console.log(value);
        return value;
    }
    
  • any类型
    • 丢失类型约束
    function log(value: any) {
        console.log(value);
        return value;
    }
    
  • 泛型
    • 不预先确定的类型,使用时才确定

    泛型:不预先确定的数据类型,具体的类型在使用时的时候才能确定

泛型函数

  • 定义
    • function generic(arg:T):T
  • 调用
    • generic(arg) 指明调用类型,添加类型参数
    • generic(arg) 类型推断,省略类型参数(推荐)
    function log<T>(value: T): T {
        console.log(value);
        return value;
    }
    // 调用方式
    // 指明调用类型,添加类型参数
    log<string[]>(['a', 'b']);
    // 类型推断,省略类型参数(推荐)
    log(['a', 'b']);
    
  • 泛型函数类型
    • type Generic=(arg:T)=>T
    type Log=<T>(value:T)=>T
    let myLog:Log=function(value){
        console.log(value);
        return value;
    }
    

泛型接口

  • 定义
    • interface Generic{(arg:T):T}
  • 实现
    • let generic:Generic(必须制定类型)
interface Log<T> {
    (value: T): T;
}
let myLog: Log<number> = log;
myLog(1);

// 指定默认类型
interface Log<T=string> {
    (value: T): T;
}

泛型类

  • 定义
    • class Generic{ method(value:T){}}
    • 泛型不能应用于类的静态成员(static)
  • 实例化
    • let generic = new Generic()
    • let generic = new Generic(),T可为任意类型
class Log<T> {
    public run(value: T) {
        console.log(value);
        return value;
    }
}
let log1 = new Log<number>(); // 显示的设置类型参数
let log2 = new Log(); //

泛型约束

  • T extends U(T必须具有U的属性)
// 泛型约束
interface Length {
    length: number;
}
// 输入的参数,必须要有length属性
function logfn<T extends Length>(value: T): T {
    console.log(value, value.length);
    return value;
}
logfn([1, 2, 3]);
logfn({ length: 2 });
  1. 函数和类可以轻松的支持多种类型,增强程序的扩展性
  2. 不必写多条函数重载,冗长的联合类型声明,增强代码可读性
  3. 灵活控制类型之间的约束

类型注解

作用:相当于强类型语言中的类型声明 语法:(变量/函数):type

类型检查机制

类型检查机制: TypeScript编译器在做类型检测时,所秉承的一些原则,以及表现出的一些行为. 作用:辅助开发,提供开发效果

  • 类型推断
  • 类型兼容性
  • 类型保护

类型推断

  • 含义:根据某些规则自动的为变量推断出类型
  1. 基础类型推断
    • 初始化变量
    let a=1; 
    
    • 设置函数默认参数
    let c=(x=1)=>{};
    
    • 确定函数返回值
    let c=(x=1)=>x+1;
    
  2. 最佳通用类型推断
    • 推断出一个可以兼容当前所有类型的通用类型
    let b=[1,null]
    
  3. 上下文类型推断
    • 根据事件绑定推断出事件类型
    window.onkeydown = (event) => {
        console.log(event.button);
    }
    

类型断言

  • 含义:用自己声明的类型覆盖类型推断
  • 方式:表达式 as type
    • 表达式
  • 弊端:没有按照接口的约定赋值,不会报错
// 定义接口
interface Foo {
    bar: number;
}

var foo = {} as Foo; // 类型断言
foo.bar = 1;
// 等价于
// 声明时,指定对象类型
var foo: Foo = {
    bar: 1,
};

类型兼容性

  • 含义:当一个类型Y可以被赋值给另一个类型X时,可以说类型X兼容类型Y(小兼容大)
    • X(目标类型)=Y(源类型),则X兼容Y
    // 接口兼容性
    interface X{
        a:number;
        b:number;
    }
    interface Y{
        a:number;
        b:number;
        c:number;
    }
    
    let x:X={a:1,b:2}
    let y:Y={a:1,b:2,c:3}
    x=y // 满足
    // y=x // 不满足
    

口诀: 结构之间兼容:成员少的兼容成员多的 函数之间兼容:参数多的兼容参数少的

  • 接口兼容性

    • 成员少的兼容成员多(鸭式辨型法)
  • 函数兼容性

    • 参数个数:目标函数多于源函数
    // 函数兼容性
    // 目标函数
    type Handler=(a:number,b:number)=>void
    function hof(hander:Handler){
       return hander
    }
    //1) 参数个数
    // 源函数
    let handerl=(a:number)=>{}
    hof(hander1) // 可以
    
    let hander2=(a:numer,b:number,c:number)=>{}
    hof(hander2) //不可以
    
    • 可选参数和剩余参数,遵循原则
      1. 固定参数兼容可选参数和剩余参数
      2. 可选参数不兼容固定参数和剩余参数(严格模式)
      3. 剩余参数兼容固定参数和可选参数
      // 可选参数和剩余参数
      let a=(p1:number,p2:number)=>{}
      let b={p1?:number,p2?:number}=>{}
      let c={...args:number[]}=>{}
      a=b // 兼容
      a=c // 兼容
      b=c // 不兼容
      b=a // 不兼容
      c=a // 兼容
      c=b // 兼容
      
    • 参数类型:必须匹配
    • 参数为对象
      • 严格模式: 成员多的兼容成员少的
      • 非严格模式:相互兼容(函数参数双向协变)
      // 参数类型
      let handler3 = (a:string)=>{}
      hof(handler3) //不兼容 
      // 3D兼容2D 
      interface Point3D {
         x: number;
         y: number;
         z: number;
      }
      interface Point2D {
         x: number;
         y: number;
      }
      let p3d = (point: Point3D) => {};
      let p2d = (point: Point2D) => {};
      p3d = p2d; // 兼容
      p2d = p3d; //不兼容
      
    • 返回值类型:目标函数必须与源函数相同,或为其子类型(成员少兼容成员多)
      let f = () => ({ name: 'a' });
      let g = () => ({ name: 'a', location: 'g' });
      f = g;  //兼容 成员少的兼容成员多的
      g = f; //不兼容
      
  • 枚举兼容性

    • 枚举类型和数字类型互相兼容
    • 枚举类型之间不兼容
    enum Fruit {
        apple,
        banana,
    }
    enum Color {
        red,
        yellow,
    }
    let fruit: Fruit.apple = 3; // 兼容
    let no: number = Fruit.apple; // 兼容
    let color: Color.red = Fruit.apple;//不兼容
    
  • 类兼容性

    • 静态成员和构造函数不在比较范围
    • 两个类具有相同的实例成员,它们的实例相互兼容
    • 类中包含私有成员或受保护成员,只有父类和子类的实例相互兼容
    class A {
        constructor(p: number, q: number) {}
        id: number = 1;
    }
    class B {
        static s = 1;
        constructor(p: number) {}
        id: number = 1;
    }
    let aa = new A(1, 2);
    let bb = new B(1);
    aa=bb; //兼容 静态成员和构造函数不在比较范围
    bb=aa; //兼容
    
    // 带有私人成员则互不兼容
    class A {
        constructor(p: number, q: number) {}
        id: number = 1;
        private name: string = '';
    }
    class C extends A {}
    // 父类和子类 实例 可以互相兼容
    let cc = new C(1, 2);
    aa = cc; // 兼容
    cc = aa; // 兼容
    
  • 泛型兼容性

    • 泛型接口:只有类型参数T被接口成员使用时,才会影响兼容性
    interface Empty<T> {
    }
    let obj1: Empty<number> = {};
    let obj2: Empty<string> = {};
    obj1=obj2;// 兼容
    
    interface Empty<T> {
       value:T
    }
    let obj1: Empty<number> = {};
    let obj2: Empty<string> = {};
    obj1=obj2;// 不兼容
    
    • 泛型函数:定义相同,没有指定类型参数时就兼容
    let log1 = <T>(x: T): T => {
        return x;
    };
    let log2 = <U>(y: U): U => {
        return y;
    };
    log1 = log2;
    

类型保护

  • 定义:在特定的区块中保证变量属于某种特定的类型
  • 创建区块的方法:
    if ((lang as Java).helloJava) {
        (lang as Java).helloJava();
    } else {
        (lang as JavaScript).helloJavaScript();
    }
    
    1. instanceof
      // 添加 instanceof
      if (lang instanceof Java) {
          lang.helloJava();
      } else {
          lang.helloJavaScript();
      }
      
    2. typeof
      // typeof 判断基本类型
      if (typeof x === 'string') {
          x.length;
      } else {
          x.toFixed();
      }
      
    3. in
      // 使用 in,对类添加属性
      if ('java' in lang) {
          lang.helloJava();
      } else  {
          lang.helloJavaScript();
      }
      
    4. 类型保护函数
      • 特殊的返回值:arg is type(类型谓词)
      function isJava(lang: Java | JavaScript): lang is Java { //类型谓词
          return (lang as Java).helloJava !== undefined; //类型断言
      }
      

高级类型

交叉类型(类型并集)

  • 含义:将多个类型合并为一个类型,新的类型将具有所有类型的特征
  • 应用场景:混入
     // 接口
    interface DogInterface {
        run(): void;
    }
    interface CatInterface {
        jump(): void;
    }
    // 交叉类型取并集 适合做混入
    let pet: DogInterface & CatInterface = {
        run() {},
        jump() {},
    };
    

联合类型(类型交集)

  • 含义:类型并不确定,可能为多个类型的一个
  • 应用场景:多类型支持
  • 可区分的联合类型:结合联合类型和字面量类型的保护方法
    // 联合类型 可以为多个类型的一个,类型不确定性,增强代码的灵活性
    let a: number | string = 'a';
    // 对象的联合类型
    class Dog implements DogInterface {
        run() {}
        eat() {}
    }
    class Cat implements CatInterface {
        jump() {}
        eat() {}
    }
    enum Master {
        Boy,
        Girl,
    }
    function getPet(master: Master) {
        let pet = master === Master.Boy ? new Dog() : new Cat();
        pet.eat(); // 可以访问,pet拥有Dog和Cat共同的方法eat
        pet.run(); // 不可访问
        return pet;
    }
    
    // 接口通过公共的属性,创建类型保护区块
    interface Square {
        kind: 'square';
        size: number;
    }
    interface Rectangle {
        kind: 'rectangle';
        width: number;
        height: number;
    }
    interface Circle {
        kind: 'circle';
        r: number;
    }
    // 创建联合类型
    type Shape = Square | Rectangle | Circle;
    // 1)指定明确的返回值类型
    // function area(shape: Shape): number {
    function area(shape: Shape) {
        // 通过公共的属性,创建类型保护区块
        switch (shape.kind) {
            case 'square':
                return shape.size * shape.size;
            case 'rectangle':
                return shape.width * shape.height;
            case 'circle':
                return Math.PI * shape.r * 2;
            default:
                // 2)利用throw,抛出错误
                return ((e: never) => {
                    throw new Error(e); // 抛出接口 异常错误
                })(shape);
        }
    }
    

字面类型

  • 字符串字面量
    // 字面量的联合类型
    let b: 'a' | 'b' | 'c';
    
  • 数字字面量
    // 数字的联合类型
    let c: 1 | 2 | 3;
    
  • 应用场景:限定变量取值范围

索引类型

let obj = {
    a: 1,
    b: 2,
    c: 3,
};

function getValues(obj: any, keys: string[]) {
    return keys.map(key => obj[key]);
}
console.log(getValues(obj, ['a', 'b']));//[1,2]
console.log(getValues(obj, ['e', 'f']));//[undefined,undefined]


  • 要点:
    • keyof T(索引查询操作符):类型T公共属性名的字面量联合类型
    // 索引类型的查询操作符 keyof T
    interface Obj {
        a: number;
        b: string;
    }
    let key: keyof Obj; //key是Obj 所有属性名的联合类型
    // 等同于
    let key:'a'|'b';
    
    • T[K] (索引访问操作符):对象T的属性K所代表的类型
    // T[K] 索引访问操作符
    let value: Obj['a'];
    // 等同于
    let value: number
    
    • 泛型约束
    // 添加类型约束,保证key都在obj中
    // 泛型函数
    // T extends U   继承
    function getValues<T, K extends keyof T>(obj: T, keys: K[]): T[K][] {
        return keys.map(key => obj[key]);
    }
    console.log(getValues(obj, ['e', 'f']));// 报错
    
  • 应用场景:从一个对象中选取某些属性的值

映射类型

  • 含义:从旧类型创建出新类型
  • 应用场景:
    • Readonly:将T的所有类型变为只读
    interface Obj {
        a: string;
        b: number;
        c: boolean;
    }
    type ReadonlyObj = Readonly<Obj>;
    // 等同于
    type ReadonlyObj = {
        readonly a: string;
        readonly b: number;
        readonly c: boolean;
    }
    
    • Partial:将T的所有属性变为可选
    type PartialObj = Partial<Obj>;
    // 等同于
    type PartialObj = {
        a?: string | undefined;
        b?: number | undefined;
        c?: boolean | undefined;
    }
    
    • Pick:选取以K为属性的对象T的子集
    type PickObj = Pick<Obj, 'a' | 'b'>;
    // 等同于
    type PickObj ={
        a: string;
        b: number;
    }
    
    • Record<K,T>:创新属性为K的新对象,属性值的类型为T
    type RecordObj = Record<'x' | 'y', Obj>;
    // 等同于
    type RecordObj = {
        x : Obj;
        y : Obj;
    }
    

条件类型

  • 含义:T extends U ?X:Y(如果类型T可以赋值给类型U,那么结果类型就是X,否则就是Y)
  • 应用场景:
    • Exclude<T,U>:从T中过滤掉可以赋值给U的类型
    type T = Exclude<'a' | 'b' | 'c', 'a' | 'e'>; // 取T中的不同项
    // 等同于
    type T = "b" | "c"
    
    • Extract<T,U>:从T中抽取可以赋值给U的类型
    // 从T中抽取出符合类型U的类型
    type T8 = Extract<'a' | 'b' | 'c', 'a' | 'e'>; // 取T的相同项
    // 等同于
    type T8 = "a"
    
    • NonNullable:从T中出去undefined和null
    type T7 = NonNullable<string | number | undefined | null>;
    // 等同于
    type T7 = string | number
    
    • ReturnType:获取函数的返回值类型
    // 获取函数返回值的类型
    type T9 = ReturnType<() => string>;