Typescript 【实用教程】(2024最新版)含类型声明,类型断言,函数,接口,泛型等

06-29 1386阅读

简介

TypeScript 是 JavaScript 的超集,是 JavaScript(弱类型语言) 的强类型版本。

  • 拥有类型机制
  • 文件后缀 .ts
  • Typescript = type + ES6
  • TypeScript 和 JavaScript 的关系类似 less 和 css 的关系
  • TypeScript对 JavaScript 添加了一些扩展,如 class / interface / module 等,大大提升代码的可阅读性。
  • 不能在浏览器直接执行,而是编译成 JavaScript (去掉类型和特有语法)后才会运行:

    与 JavaScript 相比的优势

    • 静态类型检查,可以在代码开发阶段就预知一些低级错误的发生。

      类型声明文件

      以为.d.ts 为后缀的文件,内容为 TS 语法编写的各种类型声明(定义数据,函数、接口或类的类型)

      • 通常安装第三方库后,若缺少类型声明文件,会有报错提示安装必要的类型声明文件

        Typescript 【实用教程】(2024最新版)含类型声明,类型断言,函数,接口,泛型等

      • 若没有提示,则可以在 https://www.npmjs.com/ 中搜索相应的类型声明文件

        搜索关键字为 @type/第三方库的名称

        Typescript 【实用教程】(2024最新版)含类型声明,类型断言,函数,接口,泛型等

      • 有的库没有类型声明文件,可以自己写,点我了解详细语法

      • 也可以用命令行生成类型文件,点我了解详情

        学习资料

        英文官网

        https://www.typescriptlang.org/

        中文文档

        https://www.tslang.cn/docs/home.html

        https://typescript.bootcss.com/

        在线开发环境

        https://www.typescriptlang.org/play/index.html

        搭建本地开发环境

        1. 新建文件夹 TSdemo

        2. 初始化项目

        npm init -y
        
        1. 安装必要的依赖
        npm i -D typescript nodemon ts-node
        
        • typescript 用于将 TS 编译成 JS
        • nodemon 用于 node 进程的重启
        • ts-node 用于直接执行 TS 类型的文件
          1. 项目目录下创建文件 index.ts
          let myNname: string = "朝阳";
          console.log(myNname);
          
          1. 修改 package.json 中的 scripts 为
            "scripts": {
              "start": "nodemon --exec ts-node index.ts"
            },
          
          1. 初始化 ts 的配置
          npx tsc --init
          

          执行成功后,会生成文件 tsconfig.json

          1. 启动项目

            Typescript 【实用教程】(2024最新版)含类型声明,类型断言,函数,接口,泛型等

            Typescript 【实用教程】(2024最新版)含类型声明,类型断言,函数,接口,泛型等

          声明类型

          值类型

          // 字符串
          let myNname: string = "朝阳";
          // 数字
          let num: number = 10;
          // 布尔类型
          let ifLogin: boolean = true;
          // 布尔类型支持赋值计算之后结果是布尔值的表达式
          let bool: boolean = !!0
          // null
          let n: null = null;
          // undefined
          let u: undefined = undefined;
          // symbol
          let s: symbol = Symbol();
          

          数组 []

          // 空数组
          let arr: [] = [];
          // 元素只能是数字的数组(其他类型的写法类似)
          let arr1: number[] = [1, 2];
          // 元素只能是数字或字符串的数组(| 表示或的关系)
          let arr2: (number | string)[] = [1, "朝阳"];
          // 即是number类型也可能是string数组
          const numbers1: number[] | string[] = ['123', '333'] // 正确
          const numbers2: number[] | string[] = [123, '333'] // 错误
          // 对象数组
          let todoList: {
            id: number;
            label: string;
            done: boolean;
          }[] = [
            {
              id: 1,
              label: "吃饭",
              done: false,
            },
            {
              id: 2,
              label: "编程",
              done: false,
            },
          ];
          // 使用类型别名(type alias)
          type User = { name: string; age: number }
          // 存储对象类型的内容
          const objectArr: User[] = [
            {
              name: 'zws',
              age: 18
            }
          ]
          // 构造函数声明类型
          let arr_1: Array = [1, 2];
          let arr_2: Array = [1, "朝阳"];
          

          对象 {}

          // 空对象
          let o: {} = {};
          let o2: object = {};
          // 必选属性的对象(赋值时,声明的属性必须有!)
          let user: {
            name: string;
            age: number;
          } = {
            name: "朝阳",
            age: 35,
          };
          // 可选属性的对象(可选属性需写在必选属性的后面!)
          let user2: {
            name: string;
            age?: number;
          } = {
            name: "晚霞",
          };
          

          Object 、 {} 、 object 的区别

          • 与Object类型相同的{}是最不具体的,可以将对象、数组和基元分配给它;

          • object是更具体的,类似于{ [key: string]: any };可以给它分配对象和数组,但不能分配原始类型的数据;

          • { [key: string]: string }是最具体的,它不允许任何原始类型、数组或具有非字符串值的对象被分配到它。

            var o: object;
            o = { prop: 0 }; // OK
            o = []; // OK
            o = 42; // Error
            o = "string"; // Error
            o = false; // Error
            o = null; // Error
            o = undefined; // Error
            var p: {}; // or Object
            p = { prop: 0 }; // OK
            p = []; // OK
            p = 42; // OK
            p = "string"; // OK
            p = false; // OK
            p = null; // Error
            p = undefined; // Error
            var q: { [key: string]: any };
            q = { prop: 0 }; // OK
            q = []; // OK
            q = 42; // Error
            q = "string"; // Error
            q = false; // Error
            q = null; // Error
            q = undefined; // Error
            var r: { [key: string]: string };
            r = { prop: 'string' }; // OK
            r = { prop: 0 }; // Error
            r = []; // Error
            r = 42; // Error
            r = "string"; // Error
            r = false; // Error
            r = null; // Error
            r = undefined; // Error
            

            类 class

            class Person {}
            const me: Person = new Person()
            class Teacher {
              name: string
              age: number
            }
            const objectArr: Teacher[] = [
              new Teacher(),
              {
                name: 'zws',
                age: 18
              }
            ]
            

            新增类型

            以下类型为 TS 新增的,在 js 中不存在的数据类型

            任意类型 any

            当不确定变量的类型时(比如来自用户输入或第三方代码库的动态内容),可以使用,但尽量少用。

            • 任何类型的值都可以赋值给 any 类型的变量
              let a: any = '你好'
              a = 0
              // 多数据类型的数组
              let list: any[] = [1, true, "free"];
              

              未知的类型 unknown

              TypeScript3.0版本新增的类型

              • 任何类型的值都可以赋值给 unknown 类型的变量
              • any 与 unknown 的区别 : unknown 需要明确类型后执行操作,any 则不需要
                let a: unknown = 1;
                let b = a + 1; // 会报错 “a”的类型为“未知”
                

                可通过 as 声明类型解决

                let a: unknown = 1;
                let b = (a as number) + 1;
                

                永不存在的值 never

                用于总会抛出异常或根本不会有返回值的函数表达式的返回值类型。

                当变量被永不为真的类型保护所约束时,该变量也是 never 类型。

                用途:

                • 限制类型
                • 控制流程
                • 类型运算
                  // 返回never的函数必须存在无法达到的终点
                  function error(message: string): never {
                      throw new Error(message);
                  }
                  // 推断的返回值类型为never
                  function fail() {
                      return error("Something failed");
                  }
                  // 返回never的函数必须存在无法达到的终点
                  function infiniteLoop(): never {
                      while (true) {
                      }
                  }
                  

                  无类型 Void

                  即什么类型都不是,通常用于描述函数没有返回值

                  function test(): void {
                  }
                  

                  void类型的变量只能被赋予undefined或null

                  let test: void = undefined;
                  

                  元组 Tuple

                  即固定长度和类型的数组

                  let x: [string, number];
                  x = ['hello', 10];
                  

                  枚举 enum

                  一种全新的数据描述方式,用于描述一组数量有限的系列数据。

                  数值枚举(默认)

                  // 枚举三原色
                  enum Color {Red, Green, Blue}
                  // 按枚举内容的字符串取值,得到的是对应的下标(类似数组的下标,从0开始)
                  let c: Color = Color.Green;  // c的值为1 
                  
                  // 枚举方向:上下左右
                  enum Direction {
                    Up,
                    Down,
                    Left,
                    Right
                  }
                  // 按下标取值,可得到枚举内容的字符串
                  console.log(Direction[0]) // "Up"
                  

                  可以自定义下标的起点

                  // 将默认的下标 0  改为 下标 1,则后续下标会依次递增
                  enum Direction {
                    Up = 1,
                    Down,  // 2
                    Left,  // 3
                    Right  // 4
                  }
                  

                  也可以自定义任意下标,未定义的在上一个的基础上递增

                  enum Direction {
                    Up = 11,
                    Down, // 12
                    Left = 6, 
                    Right, // 7
                  }
                  

                  甚至下标可以相同

                  enum Direction {
                    Up = 11,
                    Down, // 12
                    Left = 11,
                    Right, // 12
                  }
                  console.log(Direction.Down); // 打印 12
                  console.log(Direction); // 打印 { '11': 'Left', '12': 'Right', Up: 11, Down: 12, Left: 11, Right: 12 }
                  

                  如果下标使用了计算值或常量,那么该字段后面紧接着的字段必须设置初始值,不能默认递增值了!

                  const getValue = () => {
                    return 0;
                  };
                  enum ErrorIndex {
                    a = getValue(),
                    b, // error 枚举成员必须具有初始化的值
                    c
                  }
                  enum RightIndex {
                    a = getValue(),
                    b = 1,
                    c
                  }
                  const Start = 1;
                  enum Index {
                    a = Start,
                    b, // error 枚举成员必须具有初始化的值
                    c
                  }
                  

                  字符串枚举

                  枚举成员为字符串时,其之后的成员也必须是字符串。

                  enum Direction {
                    Up, // 未赋值,默认为0
                    Down = '南',
                    Left = '西',
                    Right = '东'
                  }
                  

                  类型断言 as

                  当你知道更确切的类型时,可以使用类型断言,类似类型转换,但不进行特殊的数据检查和解构。

                  它没有运行时的影响,只是在编译阶段起作用。 TypeScript会假设程序员已经进行了必须的检查。

                  let someValue: any = "this is a string";
                  

                  方式一:as 【推荐】

                  let strLength: number = (someValue as string).length;
                  

                  JSX中,只可用 as 语法断言

                  方式二:

                  let strLength: number = (someValue).length;
                  

                  函数

                  声明函数类型

                  • 如果省略参数的类型,TypeScript 会默认这个参数是 any 类型;

                  • 如果省略返回值的类型,如果函数无返回值,那么 TypeScript 会默认函数返回值是 void 类型;

                  • 如果函数有返回值,那么 TypeScript 会根据我们定义的逻辑推断出返回类型。

                  • 函数体内使用的外部变量的类型,不会体现在函数类型定义中。

                    // 命名函数
                    function add(arg1: number, arg2: number): number {
                      return x + y;
                    }
                    // 箭头函数
                    const add = (arg1: number, arg2: number): number => {
                      return x + y;
                    };
                    
                    // 定义变量 add 并声明为函数类型
                    let add: (x: number, y: number) => number;
                    add = (arg1: number, arg2: number): number => arg1 + arg2;
                    

                    使用 Interface 声明函数类型

                    interface Add {
                      (x: number, y: number): number;
                    }
                    

                    使用 type 声明函数类型

                    type Add = (x: number, y: number) => number;
                    

                    可选参数

                    可选参数需放置在必选参数之后,用 ? 标注

                    let add: Add = (arg1: number, arg2?: number): string => arg1 + arg2;
                    

                    默认参数

                    • = 号标注参数的默认值
                    • 所有必须参数后面的带默认初始化的参数都是可选的
                      function add(x: number, y: number = 20): number {
                          return x + y
                      }
                      

                      剩余参数

                      const handleData = (arg1: number, ...args: number[]) => {
                        //
                      };
                      

                      函数重载

                      强类型语言中的函数重载:定义几个函数名相同,但参数个数或类型不同的函数,在调用时传入不同的参数,编译器会自动调用适合的函数。

                      TS 中的函数重载:通过为一个函数指定多个函数类型定义,从而对函数调用的返回值进行检查。

                      • 只能用 function 来定义,不能使用接口、类型别名等。
                        // 这个是重载的一部分,指定当参数类型为string时,返回值为string类型的元素构成的数组
                        function handleData(x: string): string[];
                        // 这个也是重载的一部分,指定当参数类型为number时,返回值类型为string
                        function handleData(x: number): string; 
                        // 这个就是重载的内容了,这是实体函数,不算做重载的部分
                        function handleData(x: any): any { 
                          if (typeof x === "string") {
                            return x.split("");
                          } else {
                            return x
                              .toString()
                              .split("")
                              .join("_");
                          }
                        }
                        handleData("abc").join("_");
                        handleData(123).join("_"); // error 类型"string"上不存在属性"join"
                        handleData(false); // error 类型"boolean"的参数不能赋给类型"number"的参数。
                        

                        接口 interface

                        用于自定义任意类型

                        interface Point {
                          x: number;
                          y: number;
                        }
                        interface Point {
                          x: number,
                          y: number
                        }
                        
                        • 每个属性间的间隔,可以说 ; 也可以是,
                          interface Person {
                            name: string,
                            age: number
                          }
                          // 使用范例
                          let user: Person = {
                            name: '朝阳',
                            age: 30
                          }
                          

                          可选属性 ?

                          interface Point {
                            x: number;
                            y?: number;
                          }
                          

                          可选属性的位置没有限制,无需像函数的可选参数一样,必须放在必传参数的后面。

                          只读属性 readonly

                          interface Point {
                              readonly x: number;
                              readonly y: number;
                          }
                          

                          赋值后, x和y再也不能被改变

                          let p1: Point = { x: 10, y: 20 };
                          p1.x = 5; // error!
                          

                          属性的合并

                          interface Person {
                            name: string;
                          }
                          interface Person {
                            age: number;
                          }
                          let user: Person = {
                            name: "朝阳",
                            age: 35,
                          };
                          

                          但已经指定过类型的属性,不能改为新类型

                          interface Person {
                            name: string;
                          }
                          interface Person {
                            name: number; // 报错 后续属性声明必须属于同一类型
                          }
                          

                          若想定义为联合类型,也需在最初的地方定义!

                          interface Person {
                            name: string | number;
                          }
                          

                          定义函数类型

                          interface SumFunc {
                            (a: number, b: number): number;
                          }
                          let c: SumFunc = (a, b) => a + b;
                          let d = c(1, 2);
                          

                          定义索引类型

                          当定义了索引类型之后,数组的length方法,将不存在,包括Array原型链上的其他方法也不存

                          interface Dic {
                            [id: number]: string;
                          }
                          const dic1: Dic = {
                            0: "你",
                            1: "好",
                          };
                          const dic2: Dic = ["你", "好"];
                          

                          定义类类型(含接口的实现 implements )

                          用于给类添加约束,比如限定类必须含有某类型的属性/方法等。

                          // 定义接口 ClockInterface,有一个属性 currentTime,值类型为 Date
                          interface ClockInterface {
                            currentTime: Date;
                          }
                          // 定义类 Clock 实现接口 ClockInterface
                          class Clock implements ClockInterface {
                            // 因接口 ClockInterface 中有一个Date 类型的属性 currentTime,所以 类 Clock 也必须有这个属性
                            currentTime: Date;
                            constructor(arg: Date) {
                              // 在构造方法中,需给接口 ClockInterface 限定的属性 currentTime 赋值
                              this.currentTime = arg;
                            }
                          }
                          

                          可简写为

                          class Clock implements ClockInterface {
                            constructor(public currentTime: Date) {}
                          }
                          

                          绕开多余属性的类型检查

                          Typescript 【实用教程】(2024最新版)含类型声明,类型断言,函数,接口,泛型等

                          方式一:索引签名 【推荐】

                          interface myType {
                            name: string;
                            // 添加索引签名来兼容多余属性
                            [prop: string]: any;
                          }
                          

                          方式二:类型断言 【不推荐】

                          强行声明其为目标类型

                          let myInfo: myType = {
                            name: "朝阳",
                            age: 30,
                          } as myType;
                          

                          方式三:类型兼容 【不推荐】

                          interface myType {
                            name: string;
                          }
                          // 使用解构赋值,避开多余属性
                          let getName = ({ name }: myType) => {
                            return name;
                          };
                          let myInfo = {
                            name: "朝阳",
                            age: 30,
                          };
                          console.log(getName(myInfo));
                          

                          接口的继承 extends

                          与类的继承类似

                          // 基础类型 Person
                          interface Person {
                            name: string;
                          }
                          // Student 类型在 Person类型的基础上,新增了学号 sid
                          interface Student extends Person {
                            sid: number;
                          }
                          // Teacher 类型在 Person类型的基础上,新增了学科 class
                          interface Teacher extends Person {
                            class: string;
                          }
                          let student1: Student = {
                            name: "朝阳",
                            sid: 1,
                          };
                          let teacher1: Teacher = {
                            name: "朝阳",
                            class: "前端开发",
                          };
                          

                          同时继承多个接口

                          extends 后写多个接口即可,用 , 间隔

                          interface Shape {
                              color: string;
                          }
                          interface PenStroke {
                              penWidth: number;
                          }
                          interface Square extends Shape, PenStroke {
                              sideLength: number;
                          }
                          

                          接口继承类

                          • 接口继承了类之后,会继承成员(类型),但是不包括实现;
                          • 接口还会继承 private 和 protected 修饰的成员,但是这个接口只可被这个类或它的子类实现
                            // 定义类 Person
                            class Person {
                              name: string;
                              constructor(arg: string) {
                                this.name = arg;
                              }
                            }
                            // 接口 I 继承类 Person
                            interface I extends Person {}
                            // 接口 I 只能被 类 Person 或 类 Person 的子类实现 implements
                            class Student extends Person implements I {}
                            

                            泛型

                            在定义函数、接口或类的时候不预先指定数据类型,而在使用时再指定类型的特性。

                            泛型的作用

                            泛型可以提升应用的可重用性,如使用其创建组件,则可以使组件可以支持多种数据类型。

                            使用泛型

                            let printNum = (arg: number) => {
                              console.log(arg);
                            };
                            printNum(123);
                            printNum("123"); // 会报错,因参数只能是数字
                            
                            let printString = (arg: string) => {
                              console.log(arg);
                            };
                            printString(123); // 会报错,因参数只能是字符串
                            printString("123");
                            

                            怎样才能写一个通用的打印方法呢?

                            使用泛型!

                            let printAnyType = (arg: T) => {
                              console.log(arg);
                            };
                            printAnyType(123);
                            printAnyType("123");
                            

                            可见泛型即将类型设定为一个变量,当代码执行时传入的类型是啥,它就是啥,从而大大拓展了代码的通用性。

                            泛型的语法

                            • 用 包裹
                            • 类型的变量通常用大写字母 T 表示,也可以是任意其他大写字母

                              多个泛型

                              // 定义函数 - 让元组中的两个元素互换位置
                              let exchange = (tuple: [T, U]): [U, T] => {
                                return [tuple[1], tuple[0]];
                              };
                              let tuple1: [number, string] = [1, "朝阳"];
                              let tuple2 = exchange(tuple1);
                              console.log(tuple2);
                              
VPS购买请点击我

文章版权声明:除非注明,否则均为主机测评原创文章,转载或复制请以超链接形式并注明出处。

目录[+]