11.TS学习篇-typeScript

1. Typescript

TypeScript 是一种由微软开发的开源编程语言,它是 JavaScript 的超集,为 JavaScript 增加了静态类型系统。

在前端开发中,TypeScript 具有以下重要作用和特点:

1.优势

        1. 静态类型检查  

  • 可以在编译阶段检测出类型错误,减少运行时错误的出现概率。例如,当你试图将一个字符串赋值给一个期望为数字类型的变量时,TypeScript 编译器会报错,提醒你进行修正。
  • 增强了代码的可维护性和可读性,使开发者能够更清晰地理解代码的结构和预期的输入输出类型。

        2. 更好的代码智能感知和自动补全

  • 在现代的代码编辑器中,TypeScript 能够提供更强大的代码智能感知功能。当你输入代码时,编辑器可以根据类型信息准确地提示可能的方法、属性和变量,提高开发效率。

        3. 面向大型项目的可扩展性

  • 对于大型前端项目,代码的复杂性往往较高。TypeScript 的类型系统有助于组织和管理代码,使不同模块之间的接口更加清晰,降低模块之间的耦合度。
  • 方便团队协作,不同开发者可以更好地理解彼此的代码,减少因类型不明确而导致的错误。

        4. 与 JavaScript 完全兼容

  • TypeScript 代码最终会被编译成 JavaScript 代码,可以在任何支持 JavaScript 的环境中运行。这意味着你可以逐步将现有的 JavaScript 项目迁移到 TypeScript,而无需完全重写代码。

        5. 开发工具

                1.代码编辑器:vscode

                2. 构建工具:webpack,vite

2. Typescript基本语法

1.变量声明和类型注解

        1. 变量声明,使用 let 和 const 声明变量,与 JavaScript 类似。

let num = 5;//(可以在后续重新赋值)。
const str = 'Hello';//(一旦赋值不可更改)。

        2. 类型注解

  • 变量类型注解:使用冒号 “:” 来指定变量的类型,例如let num: number = 5;
    • 基本类型number(数字)、string(字符串)、boolean(布尔值)。例如:let age: number = 30; let name: string = 'John'; let isStudent: boolean = true;
    • 数组类型:使用 Array<类型> 或 类型[] 表示。例如:let numbers: number[] = [1, 2, 3]; 或 let names: Array<string> = ['Alice', 'Bob'];
    • 元组类型:固定长度的数组,其中每个元素的类型可以不同。例如:let person: [string, number] = ['Tom', 25];
    • 枚举类型:用于定义一组命名常量。
      enum Color {
               Red,
               Green,
               Blue
             }
             let myColor: Color = Color.Red;
  • 函数参数和返回值类型注解:在函数定义时,可以为参数和返回值添加类型注解,如function add(a: number, b: number): number { return a + b; }
  • 函数声明,可以指定参数类型和返回值类型。
    function add(a: number, b: number): number {
           return a + b;
         }
  • 可选参数和默认参数,
    • 可选参数使用 ? 表示,可传可不传。例如:
      function greet(name: string, age?: number) {... }
    • 默认参数直接在参数后赋值。
      function multiply(a: number, b = 2): number { return a * b; }
  • 箭头函数
    • 简洁的函数表达式。
      let square = (num: number) => num * num;

2.接口interface

        1.定义接口,用于定义对象的形状,可以描述对象的属性和方法。

interface Person {
     name: string;
     age: number;
   }
   let person: Person = { name: 'John', age: 30 };

        2.实现接口,类可以通过 implements 关键字实现接口。

class Student implements Person {
       name: string;
       age: number;
       constructor(name: string, age: number) {
         this.name = name;
         this.age = age;
       }
       sayHello(): void {
         console.log(`Hello, I'm ${this.name}.`);
       }
     }

3.类class

        1.类的定义,包括属性、构造函数和方法。

class Animal {
       name: string;
       constructor(name: string) {
         this.name = name;
       }
       makeSound(): void {
         console.log('Some sound');
       }
     }

        2.继承,使用 extends 关键字实现继承。

class Dog extends Animal {
       bark(): void {
         console.log('Woof!');
       }
     }

        3.访问修饰符,public(公开,默认)、private(私有,只能在类内部访问)、protected(受保护,在类内部和子类中可访问)。

class Car {
       private engine: string;
       constructor(engine: string) {
         this.engine = engine;
       }
     }
  • TypeScript 支持面向对象编程的类的概念,包括属性、方法、构造函数等。
    class Animal {
         name: string;
         constructor(name: string) {
           this.name = name;
         }
         makeSound() {
           console.log('Some sound');
         }
       }

3. Typescript泛型(Generics

在 TypeScript 中,泛型是一种强大的工具,它允许你编写可重用的代码组件,同时保持类型安全。

1.什么是泛型

泛型允许你在定义函数、类或接口的时候,不预先指定具体的类型,而是在使用的时候再指定类型。这样可以使代码更加灵活和可复用。

// 下面是一个没有使用泛型的函数:
function identity(arg: number): number {
  return arg;
}
// 这个函数只能接受和返回数字类型。如果想要一个可以处理字符串或其他类型的函数,就需要重复编写类似的代码。
// 使用泛型,可以这样写
function identity<T>(arg: T): T {
  return arg;
}
// 现在这个函数可以接受任何类型的参数,并返回相同类型的值。

2.泛型的使用场景

        1.泛型函数,可以使函数适用于多种不同的类型。可以根据不同的输入类型执行相同的逻辑。例如,一个用于数组操作的函数:

function identity<T>(arg: T): T {
       return arg;
     }
     let result = identity<string>('Hello');

        2.泛型类,类也可以使用泛型参数,可以创建通用的数据结构。

class GenericNumber<T> {
       zeroValue: T;
       add: (x: T, y: T) => T;
     }

        3.泛型接口:定义具有通用类型参数的接口。

interface KeyValuePair<K, V> {
       key: K;
       value: V;
     }
     let pair: KeyValuePair<string, number> = { key: 'age', value: 30 };

3.泛型约束

有时候,你可能需要对泛型的类型进行一些限制。可以使用泛型约束来实现这一点。例如:

interface Lengthwise {
  length: number;
}
function logLength<T extends Lengthwise>(arg: T): void {
  console.log(arg.length);
}
logLength('hello'); // 5
logLength([1, 2, 3]); // 3

在这个例子中,泛型 T 被约束为必须具有 length 属性的类型。

4. extends 

1.类的继承,使用 “extends” 关键字可以实现类的继承。子类继承父类的属性和方法,并可以添加新的属性和方法或重写父类的方法。

class Animal {
  move(): void {
    console.log('Animal is moving.');
  }
}

class Dog extends Animal {
  bark(): void {
    console.log('Woof!');
  }
}

const myDog = new Dog();
myDog.move(); // 继承自 Animal 类的方法
myDog.bark();
// Dog类继承了Animal类,所以Dog的实例可以调用move方法和自己特有的bark方法。

2.接口继承

接口也可以使用 “extends” 来继承其他接口。一个接口可以继承多个接口,合并它们的属性和方法要求。

interface Shape {
  color: string;
}

interface Square extends Shape {
  sideLength: number;
}

let square: Square = {
  color: 'blue',
  sideLength: 5
};
// 这里Square接口继承了Shape接口,所以Square类型的对象必须同时具有color和sideLength属性。

3.泛型约束

这里Square接口继承了Shape接口,所以Square类型的对象必须同时具有colorsideLength属性。

interface Lengthwise {
  length: number;
}

function logLength<T extends Lengthwise>(obj: T): void {
  console.log(obj.length);
}

logLength('hello'); // 5
logLength([1, 2, 3]); // 3

// 泛型参数T被约束为必须具有length属性的类型。这样,函数logLength只能接受具有length属性的参数。

4.TypeScript 泛型的最佳实践

        1. 提高代码复用性

                1. 创建通用的数据结构和函数,使用泛型创建一个通用的栈(Stack)数据结构,可以存储不同类型的数据:

     class Stack<T> {
       private items: T[] = [];
       push(item: T) {
         this.items.push(item);
       }
       pop(): T | undefined {
         return this.items.pop();
       }
     }
     const numberStack = new Stack<number>();
     numberStack.push(1);
     numberStack.push(2);
     const poppedNumber = numberStack.pop();
     const stringStack = new Stack<string>();
     stringStack.push('a');
     stringStack.push('b');
     const poppedString = stringStack.pop();

        2.增强类型安全性

                1.泛型约束,

  • 当你对泛型的类型有一定的要求时,可以使用泛型约束。例如,如果你希望一个泛型函数只接受具有特定属性的类型,可以这样做:
         interface HasLength {
           length: number;
         }
         function logLength<T extends HasLength>(obj: T): void {
           console.log(obj.length);
         }
         logLength('hello'); // 5
         logLength([1, 2, 3]); // 3

                2.泛型接口,

  • 定义接口时使用泛型可以提高接口的灵活性和可复用性,同时保证类型安全。例如:
         interface KeyValuePair<K, V> {
           key: K;
           value: V;
         }
         const pair: KeyValuePair<string, number> = { key: 'age', value: 30 };

        3.清晰的命名和文档

                1. 有意义的泛型名称

     class List<TElement> {
       private items: TElement[] = [];
       add(item: TElement) {
         this.items.push(item);
       }
       //...
     }

                2. 提供清晰的文档注释

     /**
      * A function that takes an array of items of type T and a callback function.
      * The callback function is applied to each item in the array.
      * @param array The array of items.
      * @param callback The function to apply to each item.
      * @returns An array of the results of applying the callback function.
      */
     function mapArray<T>(array: T[], callback: (item: T) => T): T[] {
       return array.map(callback);
     }

        4. 避免过度复杂的泛型使用

        5. 与其他 TypeScript 特性结合使用

                1. 与类型别名结合

     type Pair<T> = [T, T];
     const pair: Pair<number> = [1, 2];

                2. 与接口继承结合

     interface Animal {
       name: string;
     }
     interface Dog<T extends Animal> extends Animal {
       breed: T;
     }
     const myDog: Dog<{ name: 'Labrador', color: 'brown' }> = {
       name: 'Buddy',
       breed: { name: 'Labrador', color: 'brown' }
     };

5. infer

在 TypeScript 中,“infer” 关键字用于在条件类型中进行类型推断。它允许你从现有的类型中提取特定的部分,并在需要的时候使用这些推断出的类型。

1.提取函数返回值类型

可以使用 “infer” 来提取函数的返回值类型。

type ReturnType<T> = T extends (...args: any[]) => infer R? R : any;

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

type AddReturnType = ReturnType<typeof add>; // number

// ReturnType类型通过条件类型和 “infer” 关键字推断出函数的返回值类型。

2.处理数组元素类型

可以提取数组的元素类型。

type ArrayElementType<T> = T extends Array<infer E>? E : never;

const numbers = [1, 2, 3];
type NumberType = ArrayElementType<typeof numbers>; // number
// ArrayElementType类型提取了数组的元素类型。

3. 在复杂类型中进行推断

“infer” 可以在复杂的类型结构中进行推断。

type PromiseReturnType<T> = T extends Promise<infer R>? R : never;

const promise = new Promise<string>((resolve) => {
  resolve('Hello');
});

type PromiseResult = PromiseReturnType<typeof promise>; // string

// “infer” 用于推断 Promise 的结果类型。

4.注意事项

        1. 只能在条件类型中使用 “infer”。

        2. “infer” 的作用范围是在紧跟其后的条件类型中。如果需要在多个地方使用推断出的类型,可以将其存储在一个新的类型别名中。

        3. 过度复杂的类型推断可能会使代码难以理解。在使用 “infer” 时,要确保代码的可读性和可维护性。

总之,“infer” 是 TypeScript 中一个强大的工具,可以帮助你进行类型推断,使代码更加灵活和可维护。但在使用时要谨慎,避免过度复杂的类型操作。

// 动态获取函数参数的类型
function test (name: string) {}

// 用ts提取函数的参数类型
type Fn = (name: [string, number, Date]) => void
const t: Fn  = (name) => {}

// 正向定义可以这样,但是我们需要反向推导

// 1.首先使用infer
// 2.然后要用extends约束,理解为判断
// 3.使用三目运算符

// 先来监测传进来的参数是否符合我的约束
// 符合的话,返回对应推导出的类型,否则,返回never
type TestParams<F> = F extends (name: infer P) => void ? P : never
// type TestParams<F> = F extends (...args: infer P) => void ? P : never
// type TestParams<F> = F extends (name: infer P, age: infer A) => void ? P : never

let f:TestParams<Fn>

  • 18
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值