TypeScript学习总结

57 阅读3分钟

简介

TypeScript 其实就是类型化的 JavaScript,它不仅支持 JavaScript 的所有特性,还在 JavaScript 的基础上添加了静态类型注解扩展。

原始类型

在 JavaScript 中,原始类型指的是非对象且没有方法的数据类型,它包括 string、number、bigint、boolean、undefined 和 symbol 这六种 (null 是一个伪原始类型,它在 JavaScript 中实际上是一个对象,且所有的结构化类型都是通过 null 原型链派生而来)。

字符串、数字、布尔值、symbol

  firstName: string = 'ahao';
  familyName: string = String('zhang');
  age: number = 30;
  isMale: boolean = true;
  symbolTest: symbol = Symbol('test');
  personInfo: string = `My name is ${this.firstName} familyName is ${this.familyName}; age is ${this.age};`;

数组、元组

TypeScript 的数组和元组转译为 JavaScript 后都是数组, 元组类型允许表示一个已知元素数量和类型的数组,各元素的类型不必相同

 nameArray:string[]=['ahao','zhang'];
  infoTuple:[string,string,number]=['ahao','zhang',30];

特殊类型

any

我们可以把任何类型的值赋值给 any 类型的变量,也可以把 any 类型的值赋值给任意类型(除 never 以外)的变量

anyTest:any
this.anyTest = this.age;
    this.anyTest = this.firstName;
    this.anyTest = this.nameArray;

unknown

与 any 不同的是,unknown 在类型上更安全。比如我们可以将任意类型的值赋值给 unknown,但 unknown 类型的值只能赋值给 unknown 或 any,

    let result: unknown;

    result = 3;
    let testUnknown: any = result;
    let testUnknown2: unknown = result;
    // let testUnknown3:number=result;
    console.log(testUnknown);

void、undefined、null、never

void 类型,它仅适用于表示没有返回值的函数

undefined 的最大价值主要体现在接口类型,它表示一个可缺省、未定义的属性

null 的价值我认为主要体现在接口制定上,它表明对象或属性可能是空值

never 表示永远不会发生值的类型;常用语报错函数及死循环

类型断言

  1. 使用 as 语法做类型断言
  2. 使用尖括号+类型的语法 <number>
    const arrayNum: number[] = [1, 2, 3, 4];
    // 使用 as 语法做类型断言 推荐使用as语法
    const greeterThan2: number = arrayNum.find(num => num > 2) as number;
    // 使用尖括号 + 类型的格式做类型断言
    const greeterThan3: number = <number>arrayNum.find(num => num > 3);

非空断言

在值(变量、属性)的后边添加 '!' 断言操作符,它可以用来排除值为 null、undefined 的情况

interface接口类型

/ ** 关键字 接口名称 */
interface ProgramLanguage {
  /** 语言名称 */
  name: string;
  /** 使用年限 */
  age: () => number;
}

可缺省属性

在接口属性后边添加 ?来表示当前属性值可缺省;当属性被标注为可缺省后,它的类型就变成了显式指定的类型与 undefined 类型组成的联合类型

/** 关键字 接口名称 */
interface OptionalProgramLanguage {
  /** 语言名称 */
  name: string;
  /** 使用年限 */
  age?: () => number;
}
let OptionalTypeScript: OptionalProgramLanguage = {
  name: 'TypeScript'
}; // ok

只读属性

通过 readonly标识该属性为只读属性

interface ReadOnlyProgramLanguage {
  /** 语言名称 */
  readonly name: string;
  /** 使用年限 */
  readonly age: (() => number) | undefined;
}
 
let ReadOnlyTypeScript: ReadOnlyProgramLanguage = {
  name: 'TypeScript',
  age: undefined
}
/** ts(2540)错误,name 只读 */
ReadOnlyTypeScript.name = 'JavaScript';

继承与实现

通过 extend关键字实现接口的继承与被继承

  interface DynamicLanguage extends ProgramLanguage {
    rank: number; // 定义新属性
  }
  
  interface TypeSafeLanguage extends ProgramLanguage {
    typeChecker: string; // 定义新的属性
  }
  /** 继承多个 */
  interface TypeScriptLanguage extends DynamicLanguage, TypeSafeLanguage {
    name: 'TypeScript'; // 用原属性类型的兼容的类型(比如子集)重新定义属性
  }
/** 类实现接口 */
{
  class LanguageClass implements ProgramLanguage {
    name: string = '';
    age = () => new Date().getFullYear() - 2012
  }
}

Type类型别名

type别名名字 = 类型定义”的格式来定义类型别名;类型别名能够实现组合类型交叉类型

  /** 联合 */
  type MixedType = string | number;
  /** 交叉 */
  type IntersectionType = { id: number; name: string; } & { age: number; name: string };
  /** 提取接口属性类型 */
  type AgeType = ProgramLanguage['age'];  

枚举

7 种常见的枚举类型:数字类型、字符串类型、异构类型、常量成员和计算(值)成员、枚举成员类型和联合枚举、常量枚举、外部枚举。

数字类型

仅仅指定常量命名的情况下,我们定义的就是一个默认从 0 开始递增的数字集合,称之为数字枚举

  enum Day {
    SUNDAY = 1,
    MONDAY,
    TUESDAY,
    WEDNESDAY,
    THURSDAY,
    FRIDAY,
    SATURDAY
  }

字符串枚举

将定义值是字符串字面量的枚举称之为字符串枚举

  enum Day {
    SUNDAY = 'SUNDAY',
    MONDAY = 'MONDAY',
    ...
  }

异构枚举

TypeScript 支持枚举类型同时拥有数字和字符类型的成员,这样的枚举被称之为异构枚举

  enum Day {
    SUNDAY = 'SUNDAY',
    MONDAY = 2,
    ...
  }

常量成员和计算(值)成员

常量成员包含:

  • 引用来自预先定义的常量成员,比如来自当前枚举或其他枚举;
  • 圆括弧 () 包裹的常量枚举表达式;
  • 在常量枚举表达式上应用的一元操作符 +、 -、~ ;
  • 操作常量枚举表达式的二元操作符 +、-、*、/、%、<<、>>、>>>、&、|、^。

除常量成员外其他的都是计算值成员

  enum FileAccess {
    // 常量成员
    None,
    Read = 1 << 1,
    Write = 1 << 2,
    ReadWrite = Read | Write,
    // 计算成员
    G = "123".length,
  }

常量枚举(const enums)

通过添加 const 修饰符定义常量枚举

  const enum Day {
    SUNDAY,
    MONDAY
  }
  const work = (d: Day) => {
    switch (d) {
      case Day.SUNDAY:
        return 'take a rest';
      case Day.MONDAY:
        return 'work hard';
    }
  }
}

外部枚举(Ambient enums)

通过 declare 描述一个在其他地方已经定义过的变量; 也可以使用 declare 描述一个在其他地方已经定义过的枚举类型,通过这种方式定义出来的枚举类型,被称之为外部枚举

declare let $: any;

$('#id').addClass('show'); // ok

泛型

泛型指的是类型参数化,即将原来某种具体的类型进行参数化。和定义函数参数一样,我们可以给泛型定义若干个类型参数,并在调用时给泛型传入明确的类型参数。设计泛型的目的在于有效约束类型成员之间的关系,比如函数参数和返回值、类或者接口成员和方法之间的关系

function reflect<P>(param: P) {
  return param;
}

泛类型

在类的定义中,我们还可以使用泛型用来约束构造函数、属性、方法的类型

class Memory<S> {
  store: S;
  constructor(store: S) {
    this.store = store;
  }
  set(store: S) {
    this.store = store;
  }
  get() {
    return this.store;
  }
}
const numMemory = new Memory<number>(1); // <number> 可缺省
const getNumMemory = numMemory.get(); // 类型是 number
numMemory.set(2); // 只能写入 number 类型
const strMemory = new Memory(''); // 缺省 <string>
const getStrMemory = strMemory.get(); // 类型是 string
strMemory.set('string'); // 只能写入 string 类型

常用的官方工具类

操作接口类型

Partial

Partial 工具类型可以将一个类型的所有属性变为可选的,且该工具类型返回的类型是给定类型的所有子集

type Partial<T> = {
  [P in keyof T]?: T[P];
};
interface Person {
  name: string;
  age?: number;
  weight?: number;
}
type PartialPerson = Partial<Person>;
// 相当于
interface PartialPerson {
  name?: string;
  age?: number;
  weight?: number;
}

Required

Required 工具类型可以将给定类型的所有属性变为必填的,映射类型在键值的后面使用了一个 - 符号,- 与 ? 组合起来表示去除类型的可选属性,因此给定类型的所有属性都变为了必填。

type Required<T> = {
  [P in keyof T]-?: T[P];
};
type RequiredPerson = Required<Person>;
// 相当于
interface RequiredPerson {
  name: string;
  age: number;
  weight: number;
}

Readonly

Readonly 工具类型可以将给定类型的所有属性设为只读,这意味着给定类型的属性不可以被重新赋值

type Readonly<T> = {
  readonly [P in keyof T]: T[P];
};
type ReadonlyPerson = Readonly<Person>;
// 相当于
interface ReadonlyPerson {
  readonly name: string;
  readonly age?: number;
  readonly weight?: number;
}

Pick

Pick 工具类型可以从给定的类型中选取出指定的键值,然后组成一个新的类型

type Pick<T, K extends keyof T> = {
  [P in K]: T[P];
};
type NewPerson = Pick<Person, 'name' | 'age'>;
// 相当于
interface NewPerson {
  name: string;
  age?: number;
}

Omit

与 Pick 类型相反,Omit 工具类型的功能是返回去除指定的键值之后返回的新类型

type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;
type NewPerson = Omit<Person, 'weight'>;
// 相当于
interface NewPerson {
  name: string;
  age?: number;
}
Exclude

Omit 类型的实现中,我们使用了 Exclude 类型。通过使用 Exclude 类型,我们从接口的所有属性中去除了指定属性,因此,Exclude 的作用就是从联合类型中去除指定的类型。

type Exclude<T, U> = T extends U ? never : T;
type T = Exclude<'a' | 'b' | 'c', 'a'>; // => 'b' | 'c'
type NewPerson = Omit<Person, 'weight'>;
// 相当于
type NewPerson = Pick<Person, Exclude<keyof Person, 'weight'>>;
// 其中
type ExcludeKeys = Exclude<keyof Person, 'weight'>; // => 'name' | 'age'

Extract

Extract 类型的作用与 Exclude 正好相反,Extract 主要用来从联合类型中提取指定的类型,类似于操作接口类型中的 Pick 类型。

type Intersect<T, U> = {
  [K in Extract<keyof T, keyof U>]: T[K];
};
interface Person {
  name: string;
  age?: number;
  weight?: number;
}
interface NewPerson {
  name: string;
  age?: number;
}
type T = Intersect<Person, NewPerson>;
// 相当于
type T = {
  name: string;
  age?: number;
};

Record

Record 的作用是生成接口类型,然后我们使用传入的泛型参数分别作为接口类型的属性和值。

type Record<K extends keyof any, T> = {
  [P in K]: T;
};
type MenuKey = 'home' | 'about' | 'more';
interface Menu {
  label: string;
  hidden?: boolean;
}
const menus: Record<MenuKey, Menu> = {
  about: { label: '关于' },
  home: { label: '主页' },
  more: { label: '更多', hidden: true },
};

Record 类型接收了两个泛型参数:**第一个参数作为接口类型的属性,第二个参数作为接口类型的属性值。**keyof any 指代可以作为对象键的属性JavaScript 仅支持 stringnumbersymbol作为对象的键值。