深入浅出 TypeScript | 青训营

81 阅读7分钟

1.1. 简单介绍

1.1.1 TypeScript VS JavaScript

两者的区别(重点!):

TypeScriptJavaScript
JavaScript的超集,用于解决大型项目的代码复杂性一种脚本语言,用于创建动态网页
强类型,支持静态和动态类型动态弱类型语言
可以在编译期间发现并修正错误只能在运行时发现错误
不允许改变变量的数据类型变量可以被赋值成不同类型

ts是js的扩展,ts能转化为js运行。

1.1.2 学习推荐

  1. TS开源教程以及应用:github.com/dzharii/awe…
  2. TS到JS在线编译:www.typescriptlang.org/play?#code/…

1.2. TS基础

1.2.1. 基本类型

  1. boolean、number、string
  2. 枚举enum
  3. any(任意类型)、unknow、void
  4. never
  5. 数据类型[]
  6. 元组类型tuple(特殊的数组,需要显示的标注出元素的类型)

1.2.2. TS基础-函数类型

  1. 定义:TS定义函数类型时要定义输入参数类型和输出类型
  2. 输入参数:参数支持可选参数(?表示)和默认参数(=表示)
  3. 输出参数:输出可以自动推断,没有返回值时,默认为void类型
  4. 函数重载:名称相同但参数不同,可以通过重载支持多种类型 例如:
function add(x: number, y: number): number {
  return x + y;
}

接受两个 number 类型参数 x 和 y,并返回一个 number 类型的值。 5. 可以使用 type 关键字或 interface 关键字来定义函数类型别名,例如

type Greeting = (name: string, age?: number) => void;

const greet: Greeting = (name, age = 30) => {
  console.log(`Hello, \${name}! You are \${age} years old.`);
};

在这个例子中,我们定义了一个 Greeting 类型的函数,它接受一个必传的 name 参数和一个可选的(问好表示) age 参数。age 参数使用了默认值,如果调用函数时未提供 age 参数,则默认为 30。

1.2.3. interface(接口)

  1. 定义:接口是为了定义对象类型
  2. 特点:可选属性(?) 只读属性(readonly) 可以描述函数类型 可以描述自定义属性
  3. 总结:接口非常灵活duck typing

interface 关键字用于定义接口,它主要用于描述对象的形状(属性和方法),例如:

interface Car {
  brand: string;
  model: string;
}

const car : Car = {
     brand: 'aodi';
     model: 'daxing';
}

1.2.4. 类

  1. 定义:写法和JS差不多,增加了一些定义
  2. 特点:增加了public(公有的,这个是默认值)、private(私有的,继承类和实例都不能调用private定义的私有属性,只能在原型上用)、protected(受保护的,仅支持在继承类和原型里被调用)修饰符
    • abstract抽象类:只能被继承,不能被实例化;作为基类,抽象方法必须被子类实现
    • interface约束类,使用implements关键字

1.3 TS进阶

1.3.1. 高级类型

  1. 联合类型 | 。例如,string | number 表示一个值可以是字符串或者数字。
  2. 交叉类型 & 。Type1 & Type2 表示一个值同时具有 Type1 和 Type2 的属性和方法。
  3. 类型断言:用于告诉编译器某个值的具体类型。它允许我们使用我们确定的类型覆盖编译器自己的类型推断。
  • 尖括号语法:使用<Type>的形式进行断言。
let value: any = "Hello World";
let length: number = (<string>value).length;    // 断言value是string类型
  • as语法:使用value as Type的形式进行断言。
let value: any = "Hello World";
let length: number = (value as string).length;   // 断言value是string类型

注意: 无论使用哪种语法形式,类型断言只影响编译阶段,不会影响运行时的对象类型。当进行类型断言时,我们需要确保类型断言的类型和实际类型是兼容的,否则可能会导致运行时错误。

  • 非空断言(Non-null Assertion):用于告诉编译器一个值一定不为 null 或 undefined。使用!来进行非空断言。
let name: string | undefined = getUsername();
let length: number = name!.length;   // 告诉编译器name一定有值
  • 类型守卫(Type Guard):通过检查运行时的条件来缩小变量的类型范围,不需要显式的类型断言。
interface Cat {
  meow(): void;
}

interface Dog {
  bark(): void;
}

function makeSound(animal: Cat | Dog) {
  if ("meow" in animal) {  //使⽤这种 "in" 操作符可以根据运⾏时的条件来缩窄变量的类型范围,
    animal.meow();
  } else {
    animal.bark();
  }
}

上述例子中,定义了两个接口 Cat 和 Dog,它们分别表示猫和狗,并且都有一个方法,meow() 和 bark()。

makeSound 是一个函数,它接受一个参数 animal,类型为 Cat 或 Dog 的联合类型。在函数内部,它使用 if ("meow" in animal) 条件语句来检查 animal 是否具有 meow() 方法。如果 animal 接受传来的实参 是 Cat 类型,则执行 animal.meow() 方法;如果 animal 是 Dog 类型,则执行 animal.bark() 方法。

注意: 过度使用类型断言可能会导致类型不安全。应该在确保类型一致性的情况下谨慎使用类型断言。

  1. 类型别名(type VS interface ):
  • 定义:给类型起个别名

  • 相同点:

    • 都可以定义对象或函数
    • 都允许继承
  • 差异点:

    • interface是TS用来定义对象的,type是用来定义别名方面使用
    • type可以定义基本类型,interface不行
    • interface可以合重复声明,type不行
  1. 字面量类型(Literal Types):字面量类型允许我们指定一个具体的值作为类型。例如,type Color = "red" | "green" | "blue" 定义了一个名为 Color 的新类型,它只能取 "red"、"green" 或 "blue" 这三个值中的一个。

1.3.2 泛型

什么时候需要泛型

软件工程中,我们不仅要创建一致的定义良好的API,同时也要考虑可重用性

组件不仅能够支持当前的数据类型,同时也能支持未来的数据类型,这在创建大型系统时为你提供了十分灵活的功能

可以参考C++中的模板函数来理解。

泛型的定义

允许我们在定义函数、类或接口时使用参数化类型

基本使用

  1. 泛型的语法是<>里面写类型参数,一般用T表示
  2. 使用时有两种方法指定类型:
    • 定义要使用的类型
    • 通过TS类型推断,自动推导类型
  3. 泛型的作用是临时占位,之后通过传来的类型进行推导 例如:
// 泛型函数
function identity<T>(arg: T): T {
  return arg;
}
let result = identity<number>(10);
console.log(result); // 输出:10

基础操作符

  • typeof:获取类型
  • keyof:获取所有键,也就是一个类型的所有属性名构成的联合类型。
  • in:遍历枚举类型
  • T[K]:索引访问
  • extends:泛型约束。 我们可以限制泛型参数只能是特定类型或者实现了特定接口的类型。这样可以确保传入的参数满足一定的条件。
function printName<T extends { name: string }>(obj: T) {
  console.log(obj.name);
}
printName({ name: "Alice", age: 25 }); // 输出:Alice

在上述示例中,泛型约束 T extends { name: string } 表示泛型参数 T 必须满足一个对象类型的约束,即对象类型必须包含 name 属性,并且它的类型是 string。这样,在 printName 函数中就可以安全地访问 obj.name 属性。

常用工具类型

  • Partial:将类型属性变为可选
  • Required:将类型属性变为必选
  • Readonly:将类型属性变为只读
  • Pick<T, K> 、Record<T, K> ...
    • Pick<T, K> 是一个从泛型类型 T 中选取部分属性的工具类型。它接受两个参数:T 表示源类型,K 表示要选取的属性名的联合类型。
    • Record<K, T> 是一个创建具有指定键类型 K 和值类型 T 的对象的工具类型。它接受两个参数:K 表示键的类型,T 表示值的类型。
type Fruit = 'apple' | 'banana' | 'orange';
type FruitInventory = Record<Fruit, number>;

const inventory: FruitInventory = {
  apple: 5,
  banana: 3,
  orange: 7
};

在上述示例中,Record<Fruit, number> 创建了一个类型 FruitInventory,它是以 Fruit 类型中的每个值为键,以 number 类型为值的对象类型。所以 inventory 变量是一个具有 apple、banana 和 orange 三个键的对象,对应的值都是 number 类型。

1.4 TS实战

1.4.1 声明文件

  • declare:三方库需要类型声明文件

  • .d.ts:声明文件定义

  • @types:三方库TS类型包

  • tsconfig.json:定义了TS的配置