Skip to content

接口

TS 中接口可以用于对类的一部分行为进行抽象,也常用于对对象的形状进行描述。 使用 interface 关键字创建,首字母必须大写。

  • 可选属性:该属性可以不存在
  • 任意属性:一旦定义了任意属性,那么确定属性和可选属性的类型都必须是它的类型的子集
  • 只读属性:只读的约束存在于第一次给对象赋值的时候,而不是第一次给只读属性赋值的时候
typescript
interface User {
  name: string;
  age?: number;
  readonly isMale: boolean;
  say: (words: string) => void;
  phone: {
    [name: string]: string;
  };
}

const richard: User = {
  name: 'Richard',
  age: 20,
  isMale: true,
  say: (words) => {
    console.log(words);
  },
  phone: {
    移动: '1111111',
    联通: '1111111',
    电信: '1111111',
  },
};
richard.age = 22;
// richard.isMale = true
// richard.name = undefined
richard.age = undefined;
richard.say('666');

// interface 继承
interface VIPUser extends User {
  isVIP: boolean;
}

const superRichard: VIPUser = {
  name: 'Richard',
  age: 20,
  isMale: true,
  say: (words) => {
    console.log(words);
  },
  phone: {
    移动: '1111111',
    联通: '1111111',
    电信: '1111111',
  },
  isVIP: false,
};

TS 中除了实现 ES6 的类还对它进行了扩展

  • public:修饰的属性或方法是公有的,可以在任何地方被访问,默认的所有的属性和方法都是 public 的
  • private:修饰的属性和方法是私有的,不能在声明它的类的外部被访问
  • protected:修饰的属性或方法是受保护的,它只能在类的内部和子类中被访问
typescript
class Car {
  protected run() {
    console.log('run');
  }
  // 类的内部
  move() {
    this.run();
  }
}
class GTR extends Car {
  init() {
    // 子类
    this.run();
  }
}
const car = new Car();
const gtr = new GTR();
// car.run()
gtr.init();
// gtr.run()
  • 抽象类: abstract 用于定义抽象类和其中的抽象方法,抽象类是不允许被实例化的。抽象类的抽象方法只能由子类实现。
typescript
abstract class Animal {
  abstract makeSound(): void;
  move(): void {
    console.log('move');
  }
}

// const animal = new Animal()
class Cat extends Animal {
  makeSound(): void {
    console.log('miao~~~');
  }
}

const cat = new Cat();
cat.makeSound();
cat.move();

类与接口

  • 类实现接口

    一个类只能继承自另一个类,有时候不同的类之间可以有一些共同的特性,这时候就可以把特性提取成接口 用 implements 关键字来实现。一个类可以实现多个接口。

typescript
interface Alarm {
  alert: () => void;
}
interface Light {
  lightOn: () => void;
  lightOff: () => void;
}
class Car implements Alarm, Light {
  alert() {
    console.log('Car alert');
  }
  lightOn() {
    console.log('Car light on');
  }
  lightOff() {
    console.log('Car light off');
  }
}
const car = new Car();
car.alert();
car.lightOff();
car.lightOn();
  • 接口继承接口:子接口中包含父接口的形状
  • 接口继承类
typescript
class Point {
  x: number = 0;
  y: number = 0;
}
interface Point3d extends Point {
  z: number;
}
let point3d: Point3d = { x: 1, y: 2, z: 3 };
// 类作为接口的时候可以同时赋予实例的初始值
const point = new Point();

函数

  • 函数声明
typescript
function sum(x: number, y: number): number {
  return x + y;
}
sum(1, 3);
// 输入多余(或少于)要求的参数是不允许的
  • 函数表达式
typescript
// sum的类型通过类型推论推断出来的
let sum = function (x: number, y: number): number {
  return x + y;
};
// 显示定义
let mySum: (x: number, y: number) => number = function (x: number, y: number): number {
  return x + y;
};
  • 用 interface 定义函数的形状
typescript
interface SearchFunc {
  (source: string, substring: string): boolean;
}

let mySearch: SearchFunc = function (source, substring) {
  return source.search(substring) !== -1;
};
  • 可选参数:可选参数后面不允许再出现必须参数
typescript
const add = (a: number, b?: number): number => a + b;
  • 参数默认值:TS 会将添加了默认值的参数识别为可选参数,且它后边能跟必传参数
typescript
const add = (a: number, b: number = 0): number => a + b;
  • 剩余参数:它只能是最后一个参数
typescript
function push(a: number, ...rest: number[]) {}
  • 重载:允许一个函数接受不同数量或者类型的参数,解决多个可选参数时函数的调用方式
typescript
interface IDirection {
  top: number;
  right: number;
  bottom: number;
  left: number;
}

function assigned(all: number): IDirection;
function assigned(topAndBottom: number, leftAndRight: number): IDirection;
function assigned(top: number, right: number, bottom: number, left: number): IDirection;

function assigned(a: number, b?: number, c?: number, d?: any) {
  if (b === undefined && c === undefined && d === undefined) {
    b = c = d = a;
  } else if (c === undefined && d === undefined) {
    c = a;
    d = b;
  }
  return {
    top: a,
    right: b,
    bottom: c,
    left: d,
  };
}

assigned(1);
assigned(1, 2);
assigned(1, 2, 3, 4);
// 无此类型的调用方式
// assigned(1,2,3)
// assigned(1,2,undefined,3)

泛型

泛型是指在定义函数,接口或者类的时候不预先指定具体的类型,而在使用的时候再指定类型的一种特性。

typescript
function createArray<T\>(length: number, value: T): Array<T\> {
  let result: T[] = [];
  for (let i = 0; i < length; i++) {
    result[i] = value;
  }
  return result;
}
createArray<string\>(3, 'a');
// 或
createArray(3, 'a');
  • 多个类型参数
typescript
function swap<T, U\>(tuple: [T, U]): [U, T] {
  return [tuple[1], tuple[0]];
}
swap([7, 'seven']);
  • 泛型约束:由于在使用泛型的时候事先不知道它是那种类型,所以不能随意操作它的属性和方法
typescript
function getArrayLength<T\>(arg: Array<T\>): Array<T\> {
  console.log(arg.length);
  return arg;
}
  • 泛型接口
typescript
interface CreateArrayFunc {
  <T\>(length: number, value: T): Array<T\>;
}
const createArrayFunc: CreateArrayFunc = function <T\>(length: number, value: T): Array<T\> {
  let result: T[] = [];
  for (let i = 0; i < length; i++) {
    result[i] = value;
  }
  return result;
};
createArrayFunc(3, 'x');
  • 泛型类
typescript
class Stack<T\> {
  private arr: T[] = [];

  public push(item: T) {
    this.arr.push(item);
  }

  public pop() {
    this.arr.pop();
  }
}

const stack = new Stack<string\>();
stack.push('string');
// stack.push(1) 报错
  • 泛型参数的默认类型
typescript
function createArray<T = string\>(length: number, value: T): Array<T\> {
  let result: T[] = [];
  for (let i = 0; i < length; i++) {
    result[i] = value;
  }
  return result;
}
  • 泛型约束
typescript
type Params = string | number;
class ParamsArray<T extends Params\> {
  private arr: T[] = [];
  public push(item: T) {
    this.arr.push(item);
  }
}
const paramsArray1 = new ParamsArray<string\>();
// const paramsArray2 = new ParamsArray<null\>() 报错
  • 泛型约束与索引类型
typescript
function getValue<T extends object, U extends keyof T\>(obj: T, key: U) {
  return obj[key];
}

getValue({ name: 'richard', id: 1 }, 'id');
  • 使用多重类型进行泛型约束
typescript
interface FirstInterface {
  doSomething(): number;
}

interface SecondInterface {
  doSomethingElse(): string;
}

class Demo<T extends FirstInterface & SecondInterface\> {
  // 注意,本示例是在非 「--strictPropertyInitialization」或者「--strict」下测试的
  private genericProperty: T;

  useT() {
    this.genericProperty.doSomething();
    this.genericProperty.doSomethingElse();
  }
}
  • 泛型与 new:传入一个构造函数返回它的实例

    参数 type 的类型 {new(): T} 就表示此泛型 T 是可被构造的,在被实例化后的类型是泛型 T。

typescript
function factory<T\>(type: { new (): T }): T {
  return new type();
}

function factory2<T\>(type: new () => T): T {
  return new type();
}

类型断言

类型断言不是类型转换,断言成一个联合类型不存在的类型是不允许的

  • 尖括号语法
typescript
function getLength(something: string | number): number {
  if ((<string\>something).length) {
    return (<string\>something).length;
  } else {
    return something.toString().length;
  }
}
  • as 语法
typescript
interface Person {
  name: string;
  age: number;
}

const person = {} as Person;
person.name = 'richard';
person.age = 20;

类型守卫

  • instanceof
typescript
class Person {
  name = 'richard';
  age = 20;
}

class Animals {
  name = 'pp';
  color = 'red';
}

function getSomething(ags: Person | Animals) {
  if (ags instanceof Person) {
    console.log(ags.age);
  }
}
  • in
typescript
class Person {
  name = 'richard';
  age = 20;
}

class Animals {
  name = 'pp';
  color = 'red';
}

function getSomething(ags: Person | Animals) {
  if ('color' in ags) {
    console.log(ags.color);
  }
}
  • 字面量类型守卫
typescript
type Foo = {
  kind: 'foo';
  foo: number;
};

type Bar = {
  kind: 'bar';
  bar: number;
};

function doStuff(args: Foo | Bar) {
  if (args.kind === 'foo') {
    console.log(args.foo);
    // console.log(args.bar) 报错
  }
}

交叉类型

将多个类型使用 & 合并为一个类型

联合类型

联合类型使用|分割每个类型。当 TS 不能确定一个联合类型的变量到底是哪个类型的时候,我们只能发文此联合类型的所有类型里共有的属性或方法。

typescript
let id: string | number;
id = '1';
id = 1;

类型别名

使用 type 创建类型别名,常用在联合类型

typescript
type Type = string | number;

字符串字面量类型

常用来约束取值只能是某几个字符串中的一个

typescript
type EventNames = 'click' | 'scroll';

字面量类型和类型字面量

  • 字面量类型:分为真值字面量、数字字面量、枚举字面量、大整数字面量、字符串字面量
typescript
const value1: 2 = 2;
const value2: 0b10 = 2;
const value3: 'richard' = 'richard';
const value4: false = false;
  • 类型字面量
typescript
type FOO = {
  baz: [number, 'richard'];
  toString(): string;
  bar: 1;
};

const value5: FOO = {
  baz: [1, 'richard'],
  toString: () => '',
  bar: 1,
};

可辨识联合类型

typescript
// 假如现在有一个功能,在create的时候不需要id,delete的时候需要
interface Info {
  username: string;
}

type UserAction =
  | {
      id: number;
      action: 'delete';
      info: Info;
    }
  | {
      action: 'create';
      info: Info;
    };

const createAction: UserAction = {
  action: 'create',
  // id: 1, 报错
  info: {
    username: 'richard',
  },
};

const deleteAction: UserAction = {
  action: 'delete',
  id: 1,
  info: {
    username: 'richard',
  },
};

const UserReducer = (userAction: UserAction) => {
  switch (userAction.action) {
    case 'create':
      console.log(userAction.info);
      break;
    case 'delete':
      console.log(userAction.id);
      break;
  }
};

明确赋值断言

明确赋值断言是一项功能,它允许将!放置在实例属性和变量声明之后,来表明此属性已经确定它已经被赋值了

typescript
class Strict {
  foo: number;
  baz: boolean = true;
  bar!: string;
  constructor() {
    this.foo = 1;
  }
}

let initValue: number;
init();
console.log(initValue! + initValue!);
function init() {
  initValue = 10;
}

is 关键字

typescript
function isString(test: any): test is string {
  return typeof test === 'string';
}

function example(foo: number | string) {
  if (isString(foo)) {
    // 去掉 test is string 会报错
    console.log(foo.length);
  }
}

可调用类型注解

typescript
interface ToString {
  new (): string;
}

declare const SomethingToString: ToString;
new SomethingToString();

索引类型: keyof

typescript
// 取出 obj 对象上的一些属性并返回这些属性的数组
function pick<T, K extends keyof T\>(obj: T, names: K[]): T[K][] {
  return names.map((n) => obj[n]);
}

const user = {
  username: 'richard',
  id: 1,
};

const res = pick(user, ['id']);

映射类型: K in keys

typescript
interface IUser {
  username: string;
  id: number;
}

type partial<T\> = { [K in keyof T]?: T[K] };
type partialUser = partial<IUser\>;

const user1: partialUser = {
  id: undefined,
};

条件类型: T extends U ? X : Y

T extends U ? X : Y 表示为若 T 能够赋值给 U,那么类型是 X,否则为 Y

  • 条件类型的使用
typescript
declare function f<T extends boolean\>(x: T): T extends true ? string : number;

const x = f(Math.random() < 0.5);
const y = f(false);
const z = f(true);

infer 关键字

  • 条件类型与 infer
typescript
// 如果 T 能赋值给 (param: infer P) => any,则结果是(param: infer P) => any类型中的参数 P,否则返回为 T,infer P表示待推断的函数参数.
type ParamType<T\> = T extends (param: infer P) => any ? P : T;
  • infer 的应用
    • 元组转联合类型
    • 联合类型转交叉类型
typescript
type ElementOf<T\> = T extends Array<infer E\> ? E : never;

type TTuple = [string, number];

type ToUnion = ElementOf<TTuple\>; // string | number

type UnionToIntersection<U\> = (U extends any ? (k: U) => void : never) extends (k: infer I) => void
  ? I
  : never;

type Result = UnionToIntersection<string | number\>;

常用工具类型

内置工具类型

可查看 lib.es5.d.ts

  • keyof

  • typeof

  • +/-: 在映射类型中给属性添加修饰符

  • Partial<T>: 将属性全部变为可选属性(无法转换深层次的属性)

typescript
// 内部实现
type Partial<T\> = {
  [P in keyof T]?: T[P];
};
// 示例
interface AccountInfo {
  name: string;
  email: string;
  age: number;
}
const model: Partial<AccountInfo\> = {
  name: 'richard',
};
  • Required<T>: 将 T 中的所有属性转换为必选属性
typescript
// 内部实现
type Require<T\> = {
  [P in keyof T]-?: T[P];
};
// 示例
interface AccountInfo {
  name?: string;
  email?: string;
  age?: number;
}
const model: Require<AccountInfo\> = {
  name: 'richard',
  email: '',
  age: 1,
};
  • Readonly<T>: 将所有的属性标记为只读属性
typescript
// 内部实现
type Readonly<T\> = {
  readonly [P in keyof T]: T[P];
};
// 示例
interface AccountInfo {
  name: string;
  email: string;
  age: number;
}
const model: Readonly<AccountInfo\> = {
  name: 'richard',
  email: '',
  age: 1,
};
// model.age = 20 报错
  • Pick<T, K>: 从 T 中过滤出属性 K
typescript
// 内部实现
type Pick<T, K extends keyof T\> = {
  [P in K]: T[P];
};
// 示例
interface AccountInfo {
  name: string;
  email: string;
  age: number;
}
const model: Pick<AccountInfo, 'name' | 'age'\> = {
  name: 'richard',
  age: 1,
};
  • Exclude<T, U>: 从 T 中排除那些可以赋值给 U 的类型
typescript
// 内部实现
type Exclude<T, U\> = T extends U ? never : T;
// 示例
type A = Exclude<'a' | 'b' | 'c' | 'd', 'b' | 'c' | 'e'\>; // 'a' | 'd'
const a: A = 'a';
const b: A = 'd';
  • Extract<T, U>: 取 T,U 两者的交集属性
typescript
// 内部实现
type Extract<T, U\> = T extends U ? T : never;
// 示例
type A = Extract<'a' | 'b' | 'c' | 'd', 'b' | 'c' | 'e'\>; // 'b' | 'c'
  • Omit<T, K>: 忽略 T 中的某些属性
typescript
// 内部实现
type Omit<T, K extends keyof any\> = Pick<T, Exclude<keyof T, K\>\>;
// 示例
interface AccountInfo {
  name: string;
  email: string;
  age: number;
}
const account: Omit<AccountInfo, 'name' | 'age'\> = {
  email: '',
};
  • Record<K, T>: 用一组属性 K(类型 T)构造一个类型
typescript
// 内部实现
type Record<K extends keyof any, T\> = {
  [P in K]: T;
};
// 示例
type Car = 'Audi' | 'BMW' | 'Benz';
type CarList = Record<Car, { age: number }\>;

const cars: CarList = {
  Audi: { age: 119 },
  BMW: { age: 113 },
  Benz: { age: 133 },
};
  • NonNullable<T>: 排除 T 的 null 和 undefined 属性
typescript
// 内部实现
type NonNullable<T\> = T extends null | undefined ? never : T;
// 示例
const value: NonNullable<number | null | undefined\> = 1;
  • Parameters<T>: 获取一个函数的所有参数类型
typescript
// 内部实现
type Parameters<T extends (...args: any) => any\> = T extends (...args: infer P) => any ? P : never;
// 示例
interface IFun {
  (name: string, age: number): boolean;
}
type params = Parameters<IFun\>;
const value1: params[0] = 's';
const value2: params[1] = 1;
  • ConstructorParameters<T>: 获取类的构造函数参数类型
typescript
// 内部实现
type ConstructorParameters<T extends new (...args: any) => any\> = T extends new (
  ...args: infer P
) => any
  ? P
  : never;
// 示例
class PersonDemo {
  constructor(name: string, age: number) {
    console.log(name);
    console.log(age);
  }
}

type Person = ConstructorParameters<typeof PersonDemo\>;
const v123: Person = ['', 1];
  • ReturnType<T>: 获取 T 的返回类型
typescript
// 内部实现
type ReturnType<T extends (...args: any) => any\> = T extends (...args: any) => infer R ? R : any;
// 示例
type FunctionReturn = () => string;
const value: ReturnType<FunctionReturn\> = '';
  • InstanceType<T>: 获取构造函数函数类型的返回类型
typescript
// 内部实现
type InstanceType<T extends new (...args: any) => any\> = T extends new (...args: any) => infer R
  ? R
  : any;
// 示例
class C {
  x = 0;
  y = 0;
}

type T0 = InstanceType<typeof C\>; // C
type T1 = InstanceType<any\>; // any
type T2 = InstanceType<never\>; // any
type T3 = InstanceType<string\>; // Error
type T4 = InstanceType<Function\>; // Error

自定义常用类型

  • 更改类型上的属性

    使用 typescript 有时候需要重写一个库提供的 interface 的某个属性,但是重写 interface 有可能会导致冲突。

typescript
interface Test {
  name: string;
  say(word: string): string;
}

type Weaken<T, K extends keyof T\> = {
  [P in keyof T]: P extends K ? any : T[P];
};

interface Test2 extends Weaken<Test, 'name'\> {
  name: Test['name'] | number;
}
  • 数组/元组 转联合类型
typescript
const ALL_SUITS = ['hearts', 'diamonds', 'spades', 'clubs'] as const; // TS 3.4
type SuitTuple = typeof ALL_SUITS; // readonly ['hearts', 'diamonds', 'spades', 'clubs']
type Suit = SuitTuple[number]; // union type : 'hearts' | 'diamonds' | 'spades' | 'clubs'
  • 枚举 转联合类型
typescript
enum Weekday {
  Mon = 1,
  Tue = 2,
  Wed = 3,
}
type WeekdayName = keyof typeof Weekday; // 'Mon' | 'Tue' | 'Wed'
  • PartialRecord
typescript
interface Model {
  name: string;
  email: string;
  id: number;
  age: number;
}

interface Validator {
  required: boolean;
  trigger?: 'blur';
  message?: string;
}

type PartialRecord<K extends keyof any, T\> = Partial<Record<K, T\>\>;

const validateRules: PartialRecord<keyof Model, Validator\> = {
  name: { required: true, trigger: `blur` },
};
  • Unpacked: 抽离关键类型
typescript
type Unpacked<T\> = T extends (infer U)[]
  ? U
  : T extends (...args: any[]) => infer U
  ? U
  : T extends Promise<infer U\>
  ? U
  : T;

type T0 = Unpacked<string\>; // string
type T1 = Unpacked<string[]\>; // string
type T2 = Unpacked<() => string\>; // string
type T3 = Unpacked<Promise<string\>\>; // string
type T4 = Unpacked<Promise<string\>[]\>; // Promise<string\>
type T5 = Unpacked<Unpacked<Promise<string\>[]\>\>; // string

工具类型设计

  • 将深层次的属性变为可选
typescript
type DeepPartial<T\> = {
  [U in keyof T]?: T[U] extends object ? DeepPartial<T[U]\> : T[U];
};

声明文件

在使用第三方非 TS 编写的库的时候,我们需要使用它的声明文件才能获得对应的代码补全,提示功能。

使用 @types/XXX安装对应的声明文件

编写 d.ts 文件

  • declare var/let/const声明全局变量
  • declare function声明全局方法
  • declare class声明全局类
  • declare enum声明全局枚举
  • declare namespace声明含有子属性的全局对象
  • interfacetype声明全局类型
typescript
declare const JQuery: (selector: string) => any;

declare function JQuery(selector: string): any;

declare class Person {
  name: string;
  say(): string;
  constructor(name: string);
}

declare enum Directions {
  Up,
  Down,
  Left,
  Right,
}

declare namespace jQuery {
  function ajax(url: string, settings?: any): void;
}

interface AjaxSettings {
  method?: 'GET' | 'POST';
  data?: any;
}
declare namespace jQuery {
  function ajax(url: string, settings?: AjaxSettings): void;
}

声明合并

如果定义了两个相同的函数、接口或类,那他们会合并成一个类型。