Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

略读 TypeScript Deep Dive #14

Open
canvascat opened this issue Jan 26, 2021 · 1 comment
Open

略读 TypeScript Deep Dive #14

canvascat opened this issue Jan 26, 2021 · 1 comment

Comments

@canvascat
Copy link
Owner

TypeScript is a typed superset of JavaScript that compiles to plain JavaScript. Any browser. Any host. Any OS. Open source.

TypeScript 是 JavaScript 的类型的超集,本质上是在 JavaScript 上增加一套静态类型系统(编译时进行类型分析)。

  • 更强的代码提示
  • 更好的可读性
  • 编译时类型检查

使用总结

变量申明

基本类型

const is: boolean = false;
const num: number = 1;
const str: string = 'How are you.';
const arr: number[] = [1, 2, 3];
const arr2: Array<number> = [1, 2, 3];
const obj: Object = {};
const u: undefined = undefined;
const n: null = null;

类型补充

  • 枚举 Enum

使用枚举类型可以为一组数值赋予友好的名字

export enum Sources {
  API = 'api',
  SILENT = 'silent',
  USER = 'user',
}
  • 元组 Tuple

允许数组各元素的类型不必相同。 比如,你可以定义一对值分别为 string 和 number 类型的元组

// Declare a tuple type
let x: [string, number];
// Initialize it
x = ['hello', 10]; // OK
// Initialize it incorrectly
x = [10, 'hello']; // Error
  • 任意值 Any

表示任意类型,通常用于不确定内容的类型,比如来自用户输入或第三方代码库

let notSure: any = 4;
notSure = 'maybe a string instead';
notSure = false; // okay, definitely a boolean
  • 空值 Void

any 相反,通常用于函数,表示没有返回值

  • 接口 interface

类型契约,跟我们平常调服务端接口要先定义字段一个理

如下例子 point 跟 Point 类型必须一致,多一个少一个也是不被允许的
下面两个是等效的声明, 示例 A 使用内联注解,示例 B 使用接口形式:

// 示例 A
declare const myPoint: { x: number; y: number };

// 示例 B 如果有人创建了一个基于 myPoint 的库来添加新成员, 那么他可以轻松将此成员添加到 myPoint 的现有声明中
interface Point {
  x: number;
  y: number;
}
declare const myPoint: Point;
interface Point {
  x: number;
  y: number;
}
declare const myPoint: Point;

interface Point {
  z?: number;
  readonly l: number;
}

myPoint.z; // Allowed!

// 可选与只读 ? 表示可选参, readonly 表示只读
const point: Point = { x: 10, y: 20, z: 30, l: 40 };
const point2: Point = { x: '10', y: 20, z: 30, l: 40 }; // Error
const point3: Point = { x: 10, y: 20, z: 30 }; // Error
const point4: Point = { x: 10, y: 20, z: 30, l: 40, m: 50 }; // Error
point.l = 50; // error

// 类可以实现接口, 确保结构一致
class MyPoint implements Point {
  x: number;
  y: number;
  z?: number;
  readonly l: number;
}

函数

// 参数类型与返回值类型
function sum(a: number, b: number): number {
  return a + b;
}

// 配合 `interface` 使用
interface Point {
  x: number;
  y: number;
}

function distance({ x, y }: Point): number {
  return (x ** 2 + y ** 2) ** 0.5;
}

distance({ x: 3, y: 4 }); // 5

函数重载

函数重载(Function Overloading), 允许创建数项名称相同但输入输出类型或个数不同的子程序。

enum Sources {
  API = 'api',
  SILENT = 'silent',
  USER = 'user',
}

interface StringMap {
  [key: string]: any;
}
interface RangeStatic {
  index: number;
  length: number;
}

type Overload = [number, number, StringMap, Sources];

function overload(index: number, length: number, source?: Sources): Overload;
function overload(
  index: number,
  length: number,
  format: string,
  value: any,
  source?: Sources
): Overload;
function overload(
  index: number,
  length: number,
  formats: StringMap,
  source?: Sources
): Overload;
function overload(
  range: RangeStatic,
  format: string,
  value: any,
  source?: Sources
): Overload;
function overload(
  range: RangeStatic,
  formats: StringMap,
  source?: Sources
): Overload;
function overload(
  index: number | RangeStatic,
  length: number | string | StringMap,
  name?: any,
  value?: any,
  source?: Sources
): Overload {
  let formats: StringMap = {};
  if (typeof index !== 'number') {
    if (typeof length !== 'number') {
      source = value;
      value = name;
      name = length;
    }
    length = index.length;
    index = index.index;
  } else if (typeof length !== 'number') {
    source = value;
    value = name;
    name = length;
    length = 0;
  }

  if (typeof name === 'object') {
    formats = name;
    source = value;
  } else if (typeof name === 'string') {
    if (value != null) {
      formats[name] = value;
    } else {
      source = name as Sources;
    }
  }

  source = source || Sources.API;
  return [index, length, formats, source];
}

TypeScript 中的函数重载没有任何运行时开销。它只允许你记录希望调用函数的方式,并且编译器会检查其余代码。

泛型

泛型的意义在于函数的重用性,设计原则希望组件不仅能够支持当前的数据类型,同时也能支持未来的数据类型
设计泛型的关键目的是在成员之间提供有意义的约束,这些成员可以是:

  • 类的实例成员
  • 类的方法
  • 函数参数
  • 函数返回值
import Axios from 'axios';

// 请求接口数据
// 通常情况下,我们会把后端返回数据格式单独放入一个 interface 里:
interface ResponseData<T = any> {
  /** 状态码 */
  code: number;
  /** 数据 */
  result: T;
  /** 消息 */
  message: string;
}

const getUser = <T>() =>
  Axios.get<ResponseData<T>>('/somepath')
    .then((res) => res.data)
    .catch((err) => console.error(err));

// 接着我们写入返回的数据类型 User
// 这可以让 TypeScript 顺利推断出我们想要的类型:
interface User {
  name: string;
  age: number;
}

(async () => {
  // user 被推断出为
  // {
  //  code: number,
  //  result: { name: string, age: number },
  //  message: string
  // }
  const user = await getUser<User>();
})();

交叉类型

交叉类型(Intersection Types),将多个类型合并为一个类型

interface foo {
  x: number;
}
interface bar {
  b: number;
}
type intersection = foo & bar;
const result: intersection = {
  x: 10,
  b: 20,
};
const result1: intersection = {
  x: 10,
}; // error

联合类型

联合类型(Union Types),表示一个值可以是几种类型之一。

@types

DefinitelyTyped https://github.com/borisyankov/DefinitelyTyped

npm i -D @types/quill
yarn add -D @types/lodash
// tsconfig.json
{
  "compilerOptions": {
    "types": ["quill", "lodash"]
  }
}

vue-next 就使用了 Typescript,后续会继续学习它以加深对 Typescript 的了解。

@canvascat
Copy link
Owner Author

工具泛型

TypesScript 中内置了很多工具泛型,它们在 TypeScript 内置的 lib.es5.d.ts 中都有定义,所以不需要任何依赖都是可以直接使用的。
下面看看一些经常使用的工具泛型吧。(这里拿 Typescript 4.2.3 lib.es5.d.ts 举例)

/**
 * Make all properties in T optional
 */
type Partial<T> = {
  [P in keyof T]?: T[P];
};

Partial 用于将一个接口的所有属性设置为可选状态。
首先通过 keyof T,取出类型变量 T 的所有属性,然后通过 in 进行遍历,最后在属性后加上一个 ?

/**
 * Make all properties in T required
 */
type Required<T> = {
  [P in keyof T]-?: T[P];
};

Partial相反,将?替换为-?表示属性为必须选项。

/**
 * Make all properties in T readonly
 */
type Readonly<T> = {
  readonly [P in keyof T]: T[P];
};
/**
 * Construct a type with a set of properties K of type T
 */
type Record<K extends keyof any, T> = {
  [P in K]: T;
};

Record 接受两个类型变量,Record 生成的类型具有类型 K 中存在的属性,值为类型 T。这里给类型 K 加一个类型约束,extends keyof anykeyof any 就是任意类型的 key 的类型,所以为对象的索引的类型 string | number | symbol

/**
 * Exclude from T those types that are assignable to U
 */
type Exclude<T, U> = T extends U ? never : T;

/**
 * Extract from T those types that are assignable to U
 */
type Extract<T, U> = T extends U ? T : never;

Extract 表示如果 T 中的类型在 U 存在,则返回,否则抛弃;Exclude 反之。
通常我们使用keyof配合它们提取TU的公共属性或T的非公共属性。

interface Worker {
  name: string;
  age: number;
  email: string;
  salary: number;
}

interface Student {
  name: string;
  age: number;
  email: string;
  grade: number;
}

type CommonKeys = Extract<keyof Worker, keyof Student>; // 'name' | 'age' | 'email'
type SpecialKeys = Exclude<keyof Worker, keyof Student>; // 'salary'
/**
 * From T, pick a set of properties whose keys are in the union K
 */
type Pick<T, K extends keyof T> = {
  [P in K]: T[P];
};

/**
 * Construct a type with the properties of T except for those in type K.
 */
type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;

PickT中提出某些属性生成一个新的类型。
Omit 作用与Pick相反,从T中排除某些属性生成一个新的类型。

/**
 * Exclude null and undefined from T
 */
type NonNullable<T> = T extends null | undefined ? never : T;
/**
 * Obtain the parameters of a function type in a tuple
 */
type Parameters<T extends (...args: any) => any> = T extends (
  ...args: infer P
) => any
  ? P
  : never;

/**
 * Obtain the parameters of a constructor function type in a tuple
 */
type ConstructorParameters<
  T extends new (...args: any) => any
> = T extends new (...args: infer P) => any ? P : never;

用于获取函数/构造函数参数的类型。

/**
 * Obtain the return type of a function type
 */
type ReturnType<T extends (...args: any) => any> = T extends (
  ...args: any
) => infer R
  ? R
  : any;

/**
 * Obtain the return type of a constructor function type
 */
type InstanceType<T extends new (...args: any) => any> = T extends new (
  ...args: any
) => infer R
  ? R
  : any;

用于获取函数/构造函数返回值的类型。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant