TypeScript基础

发布时间:2024年01月10日

Survive by day and develop by night.
talk for import biz , show your perfect code,full busy,skip hardness,make a better result,wait for change,challenge Survive.
happy for hardess to solve denpendies.

目录

概述

需求:

设计思路

在这里插入图片描述

实现思路分析

1.TypeScript 基础类型

TypeScript 支持以下基础类型:

  1. boolean:布尔类型,表示真或假。
  2. number:数字类型,表示整数或浮点数。
  3. string:字符串类型,表示文本。
  4. Array:数组类型,表示由相同类型的元素组成的有序集合。
  5. Tuple:元组类型,表示由固定数量和类型的元素组成的数组。
  6. enum:枚举类型,表示一组具有命名值的常量。
  7. any:任意类型,表示可以赋给任意类型的值。
  8. void:空类型,表示没有任何值。
  9. null 和 undefined:表示不存在的值。
  10. never:表示永不返回的函数类型或抛出异常的函数类型。

此外,TypeScript 还支持联合类型、交叉类型、类型别名等高级类型。

2.TypeScript 变量声明

TypeScript 变量声明可以通过 letconstvar 关键字来实现。

  • let 关键字用于声明一个块级作用域的变量,它的值可以被修改。
  • const 关键字用于声明一个块级作用域的常量,它的值不能被修改。
  • var 关键字用于声明一个函数作用域的变量,它的值可以被修改。但是,它有一些特殊的行为,比如在循环中使用 var 声明的变量会被提升到循环的外部作用域。

以下是一些示例:

// 使用 let 声明变量
let name: string = "Alice";
name = "Bob"; // 可以修改

// 使用 const 声明常量
const age: number = 30;
age = 40; // 错误,常量的值不能修改

// 使用 var 声明变量
function foo() {
  var x = 10;
  if (true) {
    var x = 20; // 在同一个函数作用域中,可以重新声明同名的变量
  }
  console.log(x); // 输出 20
}
foo();

总结:

  • 推荐使用 letconst 关键字来声明变量和常量,因为它们的作用域更清晰,同时也可以避免一些潜在的 bug。
  • 避免使用 var 关键字,因为它的行为比较奇特,容易造成问题。

3.TypeScript 接口

TypeScript 接口是一种用于定义代码结构的抽象概念。它可以描述一个对象的属性和方法,以及函数的参数和返回值的类型。接口可以被类实现,表示类必须遵守接口定义的结构。接口也可以被对象直接使用,用于限制对象的结构。

接口的定义使用关键字interface,后面跟着接口的名称和属性、方法的定义。例如:

interface Person {
  name: string;
  age: number;
  sayHello: () => void;
}

上面的代码定义了一个名为Person的接口,其中有nameage属性,以及sayHello方法。可以通过实现这个接口来创建一个符合Person接口定义的类或对象。

在接口中,属性可以有不同的可选性,使用?表示可选属性。接口中的方法可以有不同的可选性和参数个数。

接口还支持继承,使用extends关键字来实现继承。例如:

interface Student extends Person {
  grade: number;
}

上面的代码定义了一个名为Student的接口,继承了Person接口的属性和方法,并新增了一个grade属性。

使用接口来定义代码结构可以提高代码的可读性和可维护性,同时还能在编译时进行类型检查,避免一些常见的错误。

4.TypeScript 类

TypeScript 是一种静态类型检查的 JavaScript 的超集,它可以在编写 JavaScript 代码的同时提供类型检查等功能。TypeScript 支持使用类来组织代码。

在 TypeScript 中,可以使用 class 关键字来声明一个类。类可以具有属性和方法。属性可以是公共的、私有的或受保护的,方法可以是公共的、私有的或受保护的。

下面是一个示例 TypeScript 类的代码:

class Person {
  private name: string;
  public age: number;

  constructor(name: string, age: number) {
    this.name = name;
    this.age = age;
  }

  public sayHello() {
    console.log(`Hello, my name is ${this.name} and I am ${this.age} years old.`);
  }
}

const person = new Person("John", 30);
person.sayHello();

在上面的例子中,Person 类有一个私有属性 name 和一个公共属性 age。它还有一个构造函数,用于初始化对象的属性。Person 类还有一个公共方法 sayHello,用于打印对象的信息。

创建一个 Person 类的实例后,可以调用 sayHello 方法来打印对象的信息。

TypeScript 类提供了一种封装和组织代码的方式,并允许使用面向对象编程的概念,如继承和多态。。

5.TypeScript 函数

TypeScript 函数可以有以下几种形式:

  1. 命名函数:使用 function 关键字定义的函数,可以通过函数名调用。
function add(x: number, y: number): number {
  return x + y;
}

let result = add(2, 3);
  1. 匿名函数:没有函数名的函数,可以将其赋值给一个变量,然后通过该变量调用。
let add = function(x: number, y: number): number {
  return x + y;
};

let result = add(2, 3);
  1. 箭头函数:使用箭头 (=>) 语法定义的函数,可以简化函数的书写。箭头函数会自动捕获其所在的上下文中的 this 值。
let add = (x: number, y: number): number => {
  return x + y;
};

let result = add(2, 3);
  1. 可选参数:在参数名称后面加上问号 (?),表示该参数为可选参数,可以省略。可选参数必须放在参数列表的最后。
function createUser(name: string, age?: number): void {
  console.log(`Name: ${name}`);
  if (age) {
    console.log(`Age: ${age}`);
  }
}

createUser("Alice");         // Name: Alice
createUser("Bob", 25);       // Name: Bob, Age: 25
  1. 默认参数:为参数指定默认值,在参数名称后面使用等号 (=) 进行赋值。
function createUser(name: string, age: number = 18): void {
  console.log(`Name: ${name}`);
  console.log(`Age: ${age}`);
}

createUser("Alice");         // Name: Alice, Age: 18
createUser("Bob", 25);       // Name: Bob, Age: 25
  1. 剩余参数:使用三个点 (…) 表示剩余参数,可以接收任意数量的参数,并将它们存储为一个数组。
function addNumbers(...numbers: number[]): number {
  let sum = 0;
  for (let num of numbers) {
    sum += num;
  }
  return sum;
}

let result = addNumbers(1, 2, 3, 4, 5);
console.log(result);         // 15

5.TypeScript 泛型

TypeScript 泛型允许我们在定义函数、类或接口的时候使用参数类型的占位符,然后在实际调用的时候再指定具体的类型。它的主要目的是增强代码的灵活性和可重用性。

在 TypeScript 中,我们可以使用 <T> 来表示一个泛型类型。泛型类型可以放在函数的参数、函数的返回值、类的属性和方法等各种位置。

例如,我们可以定义一个泛型函数来交换两个变量的值:

function swap<T>(a: T, b: T): void {
  let temp: T = a;
  a = b;
  b = temp;
}

let a: number = 1;
let b: number = 2;
swap<number>(a, b);
console.log(a, b);  // 输出 2 1

在上面的例子中,我们使用 <T> 来表示参数 ab 的类型,然后在调用函数 swap 的时候指定具体的类型为 number

除了单个类型的泛型之外,还可以使用多个泛型类型。例如,我们可以定义一个泛型函数来创建一个数组:

function createArray<T>(length: number, value: T): T[] {
  let result: T[] = [];
  for (let i = 0; i < length; i++) {
    result.push(value);
  }
  return result;
}

let array1: number[] = createArray<number>(5, 0);
console.log(array1);  // 输出 [0, 0, 0, 0, 0]

let array2: string[] = createArray<string>(3, 'hello');
console.log(array2);  // 输出 ['hello', 'hello', 'hello']

在上面的例子中,我们使用 <T> 来表示数组元素的类型,并在调用函数 createArray 的时候指定具体的类型为 numberstring

除了在函数中使用泛型之外,我们还可以在类和接口中使用泛型。例如,我们可以定义一个泛型类来表示一个队列的数据结构:

class Queue<T> {
  private items: T[] = [];

  enqueue(item: T): void {
    this.items.push(item);
  }

  dequeue(): T | undefined {
    return this.items.shift();
  }
}

let queue: Queue<number> = new Queue<number>();
queue.enqueue(1);
queue.enqueue(2);
queue.enqueue(3);

console.log(queue.dequeue());  // 输出 1
console.log(queue.dequeue());  // 输出 2
console.log(queue.dequeue());  // 输出 3

在上面的例子中,我们使用 <T> 来表示队列中元素的类型,并在创建队列对象的时候指定具体的类型为 number

总之,TypeScript 泛型为我们提供了一种灵活和可重用的方式来处理不同类型的数据。通过使用泛型,我们可以编写更通用和可扩展的代码。

5.TypeScript 枚举

TypeScript 中的枚举(enum)是一种数据类型,用于定义一组具有名称和值的常量。枚举可以帮助我们在代码中使用更直观的方式表示一组相关的常量。

以下是 TypeScript 中定义和使用枚举的基本语法:

enum Color {
  Red,
  Green,
  Blue
}

let myColor: Color = Color.Green;

在上面的代码中,我们定义了一个名为 Color 的枚举,并定义了三个常量值 RedGreenBlue。然后,我们使用 Color 类型的变量 myColor 来保存枚举常量 Green

枚举的值默认从 0 开始,依次递增。因此,Color.Red 的值为 0,Color.Green 的值为 1,Color.Blue 的值为 2。如果需要指定具体的值,可以手动为枚举常量赋值:

enum Color {
  Red = 100,
  Green = 200,
  Blue = 300
}

枚举的值可以是任意类型,不仅限于数字。例如,可以使用字符串类型作为枚举的值:

enum Direction {
  Up = "UP",
  Down = "DOWN",
  Left = "LEFT",
  Right = "RIGHT"
}

枚举在编译为 JavaScript 代码时会被转换为对象。因此,可以通过枚举的名称来访问枚举常量的值,也可以通过枚举常量的值来获取对应的枚举名称:

console.log(Color.Red); // 输出:0
console.log(Color[0]); // 输出:Red

总结一下,TypeScript 的枚举提供了一种简单、直观的方式来定义和使用一组常量,可以提高代码的可读性和维护性。

TypeScript 类型推论

TypeScript类型推论是指在编写代码时,TypeScript根据变量的初始化值和使用方式来推断其类型。换句话说,当我们声明一个变量并初始化时,TypeScript会根据我们提供的值来推断该变量的类型。例如:

let num = 10; // TypeScript会推断num的类型为number
let str = "Hello"; // TypeScript会推断str的类型为string
let bool = true; // TypeScript会推断bool的类型为boolean

TypeScript类型推论还可以根据变量的使用方式来推断其类型。例如:

function add(a, b) {
  return a + b;
}

let result = add(10, 5); // TypeScript会推断result的类型为number

在这个例子中,TypeScript推断出ab的类型为any,因为它们没有明确的类型注解。然后,TypeScript根据add函数的返回值推断出result的类型为number

类型推论在很多情况下可以简化代码,不需要显式地指定变量的类型,而是让TypeScript根据上下文推断出类型。但在某些情况下,为了明确变量的类型,我们还是可以使用类型注解来指定变量的类型。

TypeScript 类型兼容性

TypeScript的类型兼容性是指在类型检查期间,如果一个类型A可以赋值给另一个类型B,那么就认为类型A兼容类型B。

TypeScript的类型兼容性规则如下:

  1. 普通类型的兼容性:如果A的类型是B的子类型,或者A和B是相同类型,那么A兼容B。

  2. 函数参数的兼容性:如果一个函数的参数类型是另一个函数的参数类型的子类型,那么这两个函数是兼容的。

  3. 可选参数和剩余参数的兼容性:可选参数可以兼容必选参数,而剩余参数可以兼容任意个数的参数。

  4. 函数返回值的兼容性:如果一个函数的返回值类型是另一个函数的返回值类型的父类型,那么这两个函数是兼容的。

  5. 数组的兼容性:如果一个数组的元素类型是另一个数组的元素类型的子类型,那么这两个数组是兼容的。

  6. 对象的兼容性:如果一个对象的属性和方法是另一个对象的子集,那么这两个对象是兼容的。

  7. 类的兼容性:如果一个类的属性和方法是另一个类的子集,那么这两个类是兼容的。

  8. 泛型的兼容性:如果一个泛型类型参数在一个类型中是协变的,那么这个类型参数可以兼容另一个类型。

通过类型兼容性,TypeScript可以进行更严格的类型检查,减少潜在的错误。

TypeScript 高级类型

TypeScript 是一种静态类型检查的 JavaScript 超集,它提供了一些高级类型特性,以帮助开发者更精确地描述和操作数据类型。下面是一些 TypeScript 的高级类型:

  1. 泛型(Generics):允许函数、类和接口在操作多种类型的数据时保持类型安全性。可以使用泛型来创建可重用的组件,而不必指定具体的数据类型。

  2. 联合类型(Union Types):允许变量具有多个可能的数据类型。可以使用联合类型来定义一个变量可以接受多种类型的值。

  3. 交叉类型(Intersection Types):允许将多个类型合并成一个新的类型。可以使用交叉类型来创建具有多个特征的对象或函数。

  4. 类型别名(Type Aliases):允许为复杂的类型定义一个简洁的别名。可以使用类型别名来提高代码的可读性和可维护性。

  5. 字面量类型(Literal Types):允许变量只接受特定的字面量值。可以使用字面量类型来限制变量的取值范围,并提供更严格的类型检查。

  6. 可辨识联合(Discriminated Unions):允许通过共同的属性来区分不同的联合类型成员。可以使用可辨识联合来创建更精确的类型判断和条件分支。

  7. 条件类型(Conditional Types):允许类型根据条件进行推断和操作。可以使用条件类型来实现复杂的类型转换和条件判断。

这些高级类型特性可以帮助开发者在编写 TypeScript 代码时更加灵活和类型安全。可以根据具体的需求来选择合适的高级类型,以提高代码的可读性、可维护性和性能。

TypeScript 迭代器和生成器

在TypeScript中,迭代器和生成器是用于处理集合中的元素的功能。迭代器是一个对象,它实现了next()方法并返回一个包含value和done属性的对象。生成器是一个特殊的函数,它使用关键字yield来定义可迭代的值。生成器函数内部可以使用yield语句来暂停函数的执行,并返回一个包含value和done属性的对象。

迭代器的使用示例:

class MyIterator {
  private data: any[];
  private index: number;
  
  constructor(data: any[]) {
    this.data = data;
    this.index = 0;
  }
  
  next(): { value: any, done: boolean } {
    if (this.index < this.data.length) {
      return {
        value: this.data[this.index++],
        done: false
      };
    } else {
      return {
        value: undefined,
        done: true
      };
    }
  }
}

const myIterator = new MyIterator([1, 2, 3]);

console.log(myIterator.next()); // { value: 1, done: false }
console.log(myIterator.next()); // { value: 2, done: false }
console.log(myIterator.next()); // { value: 3, done: false }
console.log(myIterator.next()); // { value: undefined, done: true }

生成器的使用示例:

function* myGenerator() {
  yield 1;
  yield 2;
  yield 3;
}

const iterator = myGenerator();

console.log(iterator.next()); // { value: 1, done: false }
console.log(iterator.next()); // { value: 2, done: false }
console.log(iterator.next()); // { value: 3, done: false }
console.log(iterator.next()); // { value: undefined, done: true }

使用迭代器和生成器可以更方便地处理集合中的元素,可以在循环中使用for…of语句来遍历生成器的值,而不需要使用while循环和next()方法。

TypeScript 模块

TypeScript 模块是一种组织和管理代码的方式。模块可以将代码分隔成独立的文件或文件夹,并且可以按需引入和导出模块的功能。

在 TypeScript 中,使用模块可以实现以下目标:

  • 将代码分离成独立的文件或文件夹,提高代码的可读性和可维护性。
  • 隐藏内部实现细节,只暴露外部接口,提供更好的封装性。
  • 实现代码的重用,可以在不同的项目中共享模块。
  • 提供依赖管理和版本控制的能力。

在 TypeScript 中,可以使用两种模块系统:CommonJS 和 ES6 模块。CommonJS 模块是 Node.js 中使用的模块系统,而 ES6 模块则是标准的 JavaScript 模块系统。

使用 CommonJS 模块时,可以使用 require() 函数来引入模块,使用 module.exports 来导出模块的功能:

// moduleA.ts
function sum(a: number, b: number): number {
  return a + b;
}

module.exports = sum;
// main.ts
const sum = require('./moduleA');
console.log(sum(1, 2)); // 输出: 3

使用 ES6 模块时,可以使用 importexport 关键字来导入和导出模块的功能:

// moduleA.ts
export function sum(a: number, b: number): number {
  return a + b;
}
// main.ts
import { sum } from './moduleA';
console.log(sum(1, 2)); // 输出: 3

除了这两种模块系统,TypeScript 还支持其他一些模块系统,如 AMD 和 UMD 模块系统。可以根据具体的项目需求选择适合的模块系统。

TypeScript 命名空间

在 TypeScript 中,命名空间是一种用来封装和组织代码的方式。

命名空间可以看作是一个容器,用来包含一组相关的类、接口、函数和枚举等。通过将相关代码放在同一个命名空间中,可以避免命名冲突,并且更好地组织和管理代码。

在 TypeScript 中,使用关键字 namespace 来声明一个命名空间。命名空间中可以包含多个模块(module),每个模块可以包含多个类、接口、函数等。

以下是一个使用命名空间的示例:

namespace MyNamespace {
  export class MyClass {
    // class implementation
  }

  export function myFunction() {
    // function implementation
  }
}

在上面的示例中,MyNamespace 是一个命名空间,包含了一个类 MyClass 和一个函数 myFunction。通过使用 export 关键字,可以将这些内容暴露给外部使用。

使用命名空间的时候,可以使用点运算符来访问命名空间中的成员。例如:

const myObject = new MyNamespace.MyClass();
MyNamespace.myFunction();

另外,可以使用 import 语句来导入一个命名空间中的成员,这样就可以直接使用成员,而不需要使用命名空间的前缀。

import { MyClass, myFunction } from "MyNamespace";

const myObject = new MyClass();
myFunction();

需要注意的是,命名空间和模块(module)是不同的概念。命名空间主要用于在编译时进行代码的组织和封装,而模块则主要用于在运行时进行代码的组织和加载。在实际项目中,推荐使用模块来组织和管理代码,而不是过度依赖命名空间。

TypeScript 模块解析

TypeScript 的模块解析是指在编译阶段,将导入的模块转换为可执行的 JavaScript 代码的过程。TypeScript 支持两种模块解析策略:经典解析和 Node.js 解析。

  1. 经典解析:

    • 相对路径导入:从当前文件所在目录开始,按照相对路径逐级向上查找目标模块。
    • 非相对路径导入:从配置文件中指定的路径开始,按照配置的路径逐级向上查找目标模块。
    • 配置文件:可以通过 tsconfig.json 文件来配置模块的解析策略。
  2. Node.js 解析:

    • 相对路径导入:从当前文件所在目录开始,按照相对路径逐级向上查找目标模块。
    • 非相对路径导入:首先从当前文件所在目录开始,按照相对路径逐级向上查找目标模块。如果找不到,则尝试从当前文件所在目录下的 node_modules 目录中查找目标模块。如果还找不到,则从上一级目录开始重复上述步骤,直到找到目标模块或者到达文件系统的根目录。
    • 全局模块解析:TypeScript 可以通过配置文件中的 paths 或 baseUrl 字段来指定全局模块的解析路径。

在模块解析的过程中,TypeScript 还会根据目标模块的文件类型进行相应的处理:

  • .ts 和 .tsx:直接编译为 JavaScript 代码。
  • .d.ts:将其视为声明文件,不进行编译。
  • .js 和 .jsx:只有当 allowJs 或者 checkJs 选项被设置为 true 时,TypeScript 才会对其进行编译。

总之,TypeScript 的模块解析策略非常灵活,可以根据项目的需求选择适合的解析方式。

TypeScript 声明合并

TypeScript 中的声明合并是指将多个同名的声明合并为一个声明。这可以用于合并接口、类型别名、命名空间和类等。

接口合并示例:

interface A {
  x: number;
}

interface A {
  y: number;
}

const a: A = {
  x: 1,
  y: 2
};

合并后的接口 A 相当于:

interface A {
  x: number;
  y: number;
}

类型别名合并示例:

type B = {
  x: number;
};

type B = {
  y: number;
};

const b: B = {
  x: 1,
  y: 2
};

合并后的类型别名 B 相当于:

type B = {
  x: number;
  y: number;
};

命名空间合并示例:

namespace C {
  export const x = 1;
}

namespace C {
  export const y = 2;
}

console.log(C.x, C.y); // 输出 1 2

类合并示例:

class D {
  x = 1;
}

class D {
  y = 2;
}

const d = new D();
console.log(d.x, d.y); // 输出 1 2

在声明合并时,如果存在重复的属性或方法,TypeScript 会将它们的类型合并为联合类型。如果是函数重载,则会将多个函数定义合并为一个函数定义,其中包含多个重载签名。

TypeScript 装饰器

TypeScript 装饰器是一种特殊的注释,用于修改类、方法、属性或参数的行为。装饰器提供了一种简洁的方式来给现有的代码添加新的功能。

装饰器可以应用于类、方法、属性和参数。通过在这些目标上添加装饰器,可以修改它们的行为。

装饰器使用 @ 符号来标记,后面跟着一个函数。这个函数在运行时被调用,并可以用来修改目标的行为。

例如,下面的代码演示了如何使用装饰器来修改一个类的行为:

function log(target: Function) {
  console.log(`Logging from ${target.name}`);
}

@log
class MyClass {
  // class implementation
}

在这个例子中,log 装饰器被应用在 MyClass 上。当 MyClass 被定义时,log 装饰器的函数会被调用,打印出 “Logging from MyClass”。

除了类装饰器,还有方法装饰器、属性装饰器和参数装饰器。方法装饰器可以修改方法的行为,属性装饰器可以修改属性的行为,参数装饰器可以修改方法参数的行为。

装饰器可以被链式调用,也可以传递参数。例如:

function log(target: Function) {
  console.log(`Logging from ${target.name}`);
}

function debug(enabled: boolean) {
  return function(target: Function) {
    console.log(`Debugging ${enabled ? 'enabled' : 'disabled'}`);
  }
}

@log
@debug(true)
class MyClass {
  // class implementation
}

在这个例子中,debug 装饰器可以接收一个布尔值参数来决定是否启用调试。当 MyClass 被定义时,先应用 debug 装饰器,然后再应用 log 装饰器。

总的来说,TypeScript 装饰器是一种非常强大的工具,可以帮助开发人员修改现有代码的行为。它提供了一种简洁的方式来添加新的功能,使代码更易于扩展和维护。

TypeScript 三斜线指令

TypeScript 中的三斜线指令是一种特殊的注释标记,用于告诉编译器执行一些额外的操作。它们以 /// 开始,通常出现在文件的顶部。

常用的三斜线指令有三种:

  1. /// <reference path="..."/>:用于引用其他的 TypeScript 文件。当一个文件依赖于另一个文件时,可以使用该指令将它们连接起来。编译器会根据指令的顺序将文件进行编译。

  2. /// <reference types="..."/>:用于引用类型声明文件。当使用第三方库时,可以通过该指令引用对应的类型声明文件,以获得代码补全和类型检查的支持。

  3. /// <amd-module name="..."/>:用于指定模块的名称。该指令用于在 AMD 模块中指定模块的名称,以便在模块加载时使用。它一般与 importexport 语句一起使用。

三斜线指令是一种过时的写法,新版的 TypeScript 推荐使用模块导入语法和配置文件(如 tsconfig.json)来管理依赖和编译选项。因此,在新的项目中,通常不再使用三斜线指令。

参考资料和推荐阅读

参考资料
官方文档
开源社区
博客文章
书籍推荐
1.暂无

欢迎阅读,各位老铁,如果对你有帮助,点个赞加个关注呗!同时,期望各位大佬的批评指正~

文章来源:https://blog.csdn.net/xiamaocheng/article/details/135491419
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。