# Typescript 笔记

# TS 是什么

Typescript (opens new window)(以下简称 TS)是基于 javscript 语言的一种超集,本质上向这种语言加入了可选静态类型和基于类的面向对象编程。

TS 和 ECMAScript 之间的关系图如下图:

img

# TS 和 JS 的区别

TS JS
JavaScript 的超集用于解决大型项目的代码复杂性 一种脚本语言,用于创建动态网页
在编译期间发现并纠正错误 作为一种解释性语言,只能在运行时发现错误
强类型,支持动态和静态类型 弱类型,无静态类型
最终变异成 JS,可被浏览器识别 可直接运行在浏览器
支持模块、泛型、接口 不支持
生态不是很大 社区庞大

# 安装并使用

1.安装 Typescript

npm install -g typescript
# yarn
yarn global add typescript

2.编译 Typescript

创建 test.ts

const a: String = "12";

执行编译,目录会增加编译的 JS 文件

tsc test
var a = "12";

# TS 工作流程

img

通常 TS 代码在编译后都会进行一个打包处理,然后进行部署。

# TS 基本数据类型

# Boolean

let isDone: boolean = false;
// ES5:var isDone = false;

# Number

let count: number = 10;
// ES5:var count = 10;

# String

let name: string = "semliker";
// ES5:var name = 'semlinker';

# Symbol

const sym = Symbol();
let obj = {
  [sym]: "semlinker"
};

console.log(obj[sym]); // semlinker

# Array

// 1. 直接定义
let list: number[] = [1, 2, 3];
// 2. 定义泛型
let list: Array<number> = [1, 2, 3];
// 3. 定义接口
interface NumberArray {
  [index: number]: number;
}
let list: NumberArray = [1, 2, 3];

// 类数组
function sum() {
  let args: IArguments = arguments;
}

// 任意类型数组
let list: any[] = ["xcatliu", 25, { website: "http://xcatliu.com" }];

// 定义对象数组
const arr: { age: string }[] = [{ age: "dell" }];
// 规定某种对象格式
type User = {
  name: string;
  age: number;
};
const arr3: User[] = [{ name: "12", age: 12 }];
// 甚至可以定义一个类对象数组
class Teacher {
  name: string;
  age: number;
}
const arr4: Teacher[] = [new Teacher(), { name: "12", age: 12 }];
// 二位数组 + 元祖定义
const demo1: [string, number, string][] = [
  ["1", 2, "1"],
  ["2", 3, "3"]
];

// 解构数组
let arr: number[] = [0, 1, 2, 3];
let x: number;
let y: number;
let z: number;
[x, y, z] = arr;

# Enum

  1. 数字枚举

    enum Direction {
      NORTH,
      SOUTH,
      EAST,
      WEST
    }
    
    let dir: Direction = Direction.NORTH;
    // 默认情况下,NORTH 的初始值伟0,其他成员递增
    
    // 默认情况下编译上述 TS 得到的ES5代码
    "use strict";
    var Direction;
    (function(Direction) {
      Direction[(Direction["NORTH"] = 0)] = "NORTH";
      Direction[(Direction["SOUTH"] = 1)] = "SOUTH";
      Direction[(Direction["EAST"] = 2)] = "EAST";
      Direction[(Direction["WEST"] = 3)] = "WEST";
    })(Direction || (Direction = {}));
    var dir = Direction.NORTH;
    
  2. 字符串枚举

    enum Direction {
      NORTH = "NORTH",
      SOUTH = "SOUTH",
      EAST = "EAST",
      WEST = "WEST"
    }
    let dirName = Direction[0]; // NORTH
    let dirVal = Direction["NORTH"]; // 0 这里会出现反向映射
    
    "use strict";
    var Direction;
    (function(Direction) {
      Direction["NORTH"] = "NORTH";
      Direction["SOUTH"] = "SOUTH";
      Direction["EAST"] = "EAST";
      Direction["WEST"] = "WEST";
    })(Direction || (Direction = {}));
    
  3. 常量枚举

    添加 const 关键词即可

  4. 异构枚举

    字符串和数字混合组合

    enum Enum {
      A,
      B,
      C = "C",
      D = "D",
      E = 8,
      F
    }
    console.log(Enum.A); //输出:0
    console.log(Enum[0]); // 输出:A
    

# Any

普通类型在赋值过程中改变类型是不允许的

let myFavoriteNumber: string = "seven";
myFavoriteNumber = 7;

// index.ts(2,1): error TS2322: Type 'number' is not assignable to type 'string'.

如果是 any 类型,则允许被赋值为任意类型

let myFavoriteNumber: any = "seven";
myFavoriteNumber = 7;

任意值得属性和方法访问都是被允许的*,也允许调用任何方法

声明一个变量为任意值之后,对它的任何操作,返回的内容的类型都是任意值。

let anyThing: any = "hello";
console.log(anyThing.myName);
console.log(anyThing.myName.firstName);

# Unknown

  1. any 相似,所有类型都可以赋值给 unknown

    let value: unknown;
    
    value = true; // OK
    value = 42; // OK
    value = "Hello World"; // OK
    value = []; // OK
    value = {}; // OK
    value = Math.random; // OK
    value = null; // OK
    value = undefined; // OK
    value = new TypeError(); // OK
    value = Symbol("type"); // OK
    
  2. unknown 类型只能被赋值给 anyunknown

    let value: unknown;
    
    let value1: unknown = value; // OK
    let value2: any = value; // OK
    let value3: boolean = value; // Error
    let value4: number = value; // Error
    let value5: string = value; // Error
    let value6: object = value; // Error
    let value7: any[] = value; // Error
    let value8: Function = value; // Error
    
  3. unknown 类型的方法属性并不被允许访问

    let value: unknown;
    
    value.foo.bar; // Error
    value.trim(); // Error
    value(); // Error
    new value(); // Error
    value[0][1]; // Error
    

# Tuple

元组类型通常用来表示单个变量中存储不同类型的值,必须要类型匹配

let tupleType: [string, boolean];
tupleType = ["semlinker", true];

# Void

函数输出类型为任意类型

# Never

函数的永远不会结束执行,取到结果或者返回结果。

  • 抛出异常
  • while 脚本
function test(): never {
  throw new Error();
}
function test1(): never {
  while (true) {}
}

# Null or Undefined

let u: undefined = undefined;
let n: null = null;

# Object

# 对象类型-接口

在面向对象语言中,接口(Interfaces)是一个很重要的概念,它是对行为的抽象,而具体如何行动需要由类(classes)去实现(implement)。

interface Person {
  name: string;
  age: number;
  gender?: string; //可选类型,
  readonly id: number; //只读类型
}

let tom: Person = {
  name: "Tom",
  age: 25
};

赋值的时候,变量的形状必须和接口的形状保持一致

const getPersonName: (person: { name: string }) => string = person => person.name;
const setPersonName: (person: { name: string }, name: string) => void = (person, name) => {
  person.name = name;
};
// 改造成 Interface

interface Person {
  name: string;
}
const getPersonName1: (person: Person) => string = person => person.name;

// type 和 interface 的区别是 type可以去表示基础类型,接口只能表示对象

善用缓存变量的形式解决传参类型报错

interface Person {
  name: string;
  age?: number;
}
const getPersonName1: (person: Person) => string = person => person.name;
getPersonName1({
  name: "dell",
  age: 11
}); // 此时代码会报错,传入的是自定义格式的对象,TS 默认会进行强校验,删除可选属性

// 使用缓存对象
const person = {
  name: "dell",
  age: 11
};
getPersonName1(person); // 此时就不会报错

// 或者还有一种解决方案,添加接口任意属性
interface Person {
  name: string;
  age: number;
  [propName: string]: any;
}

# 类型别名

type 和 interface 很相似,唯一区别就是 type可以定义联合类型,type可以用工具类,也就是高级操作符

type NumberOrString = string | number

# 函数类型

# 函数声明

function sum(x: number, y: number): number {
  return x + y;
}

# 函数表达式

注意此处箭头不等于 ES6 的箭头,这里的箭头是函数的定义

在 TypeScript 的类型定义中,=> 用来表示函数的定义,左边是输入类型,需要用括号括起来,右边是输出类型。

let mySum = function(x: number, y: number): number {
  return x + y;
};
// TS 箭头表达式写法
let mySum: (x: number, y: number) => number = function(x: number, y: number): number {
  return x + y;
};

# 接口定义函数

采用函数表达式|接口定义函数的方式时,对等号左侧进行类型限制,可以保证以后对函数名赋值时保证参数个数、参数类型、返回值类型不变。

interface searchFunc {
  (x: number, y: number): number;
}
let fun: searchFunc = function(x: number, y: number): number {
  return x + y;
};

# 可选参数

function buildName(firstName: string, lastName?: string) {
  if (lastName) {
    return firstName + " " + lastName;
  } else {
    return firstName;
  }
}
let tomcat = buildName("Tom", "Cat");
let tom = buildName("Tom");

可选参数必须接在必需参数后面

# 函数参数解构写法

function add({ first, second }: { first: number; second: number }) {
  return first + second;
}

// 仅有一个参数
function add({ first }: { first: number }) {
  return 2 * first;
}

# 参数默认值

function buildName(firstName: string, lastName: string = "Cat") {
  return firstName + " " + lastName;
}
let tomcat = buildName("Tom", "Cat");
let tom = buildName("Tom");

# 重载

image-20210705160737414

# 类型断言

如果你比Typescript 更了解某个值的详细信息,可以通过类型断言告诉编辑器。

通过类型断言这种方式可以告诉编译器,“相信我,我知道自己在干什么”。类型断言好比其他语言里的类型转换,但是不进行特殊的数据检查和解构。它没有运行时的影响,只是在编译阶段起作用。

1. 尖括号用法
let someValue: any = 'this is a string'
let strLength: number = (<string>someValue).length

2. as 语法
let someValue1: any = 'this is a string'
let strLength1: number = (someValue1 as string).length
interface bird {
  fly: boolean;
  sing: () => {};
}
interface dog {
  fly: boolean;
  bark: () => {};
}

// 类型断言 你比 TS 更加知道类型,因为TS在联合类型下只会去取公有属性
// as
function trainAnial(animal: bird | dog): void {
  if (animal.fly) {
    (animal as bird).sing();
  } else {
    (animal as dog).bark();
  }
}
// in 判断属性名是否在该对象
function trainAnials(animal: bird | dog): void {
  if ("sing" in animal) {
    animal.sing();
  } else {
    animal.bark();
  }
}

非空断言

在上下文中当类型检查器无法断定类型时,一个新的后缀表达式操作符 ! 可以用于断言操作对象是非 null 和非 undefined 类型。具体而言,x! 将从 x 值域中排除 null 和 undefined 。

原本 TS 会进行类型检查,加上非空断言符号,则放弃校验

//1.忽略 undefined 和 null 类型
function myFunc(maybeString: string | undefined | null) {
  // Type 'string | null | undefined' is not assignable to type 'string'.
  // Type 'undefined' is not assignable to type 'string'.
  const onlyString: string = maybeString; // Error
  const ignoreUndefinedAndNull: string = maybeString!; // Ok
}

const a: number | undefined = undefined;
const b: number = a!;
console.log(b); // undefined 忽略undefined类型

# 类型保护

// 类型保护 typeof instanceof
let addFirst: (num1: number | string, num2: number | string) => any = (num1, num2) => {
  if (typeof num1 === "string" || typeof num2 === "string") {
    return `${num1}${num2}`;
  }
  return num1 + num2;
};

# 操作符

# 键值取 keyof

  1. 使用 keyof 获取一个类型所有键值,发回一个联合类型

    type Person = {
      name: string;
      age: number;
    };
    type PersonKey = keyof Person; // PersonKey得到的类型为 'name' | 'age'
    
  2. keyof 的一个典型用途是限制访问对象的 key 合法化,因为 any 做索引是不被接受的

    function getValue(p: Person, k: keyof Person) {
      return p[k]; // 如果k不如此定义,则无法以p[k]的代码格式通过编译
    }
    

# typeof

高级用法

const me: Person = { name: "gzx", age: 16 };
type P = typeof me; // { name: string, age: number | undefined }
const you: typeof me = { name: "mabaoguo", age: 69 }; // 可以通过编译

JStypeof 区分开,JS typeof在运行时执行,TS typeof 静态环境执行

export const useRequest = () => {
  return typeof 1 === 'number' // 运行时执行
  const { user } = useAuth();
  return (...[url, config]: Parameters<typeof request>) => // TS静态的typeof,不会运行,最终编译的代码不会包含这一句
    request(url, { ...config, token: user?.token });
};

# 遍历属性 in

[ 自定义变量名 in 枚举类型 ]: 类型

# 泛型

巧用泛型可以看VueUse源码 (opens new window)

# 语法

当我们不知道传入的参数类型时,或者是一个泛指,此时就应该使用泛型

function identity<T, U>(value: T, message: U): T {
  console.log(message);
  return value;
}

console.log(identity<Number, string>(68, "Semlinker"));

# 泛型接口

// 声明一个函数
interface GenericIdentify<T> {
  (arg: T): T;
}

# 泛型类

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

let myGenericNumber = new GGenericNumber<number>();
myGenericNumber.zeroValue = 0;
myGenericNumber.add = function(x, y) {
  return x + y;
};

# 泛型工具类型

对应文档地址 (opens new window)

# Tsconfig.json 配置

tsconfig.json 的作用

  • 用于标识 TypeScript 项目的根路径;
  • 用于配置 TypeScript 编译器;
  • 用于指定编译的文件。

tsconfig.json 重要字段

  • files - 设置要编译的文件的名称;
  • include - 设置需要进行编译的文件,支持路径模式匹配;
  • exclude - 设置无需进行编译的文件,支持路径模式匹配;
  • compilerOptions - 设置与编译流程相关的选项。

compilerOtions 选项

{
  "compilerOptions": {
    /* 基本选项 */
    "target": "es5", // 指定 ECMAScript 目标版本: 'ES3' (default), 'ES5', 'ES6'/'ES2015', 'ES2016', 'ES2017', or 'ESNEXT'
    "module": "commonjs", // 指定使用模块: 'commonjs', 'amd', 'system', 'umd' or 'es2015'
    "lib": [], // 指定要包含在编译中的库文件
    "allowJs": true, // 允许编译 javascript 文件
    "checkJs": true, // 报告 javascript 文件中的错误
    "jsx": "preserve", // 指定 jsx 代码的生成: 'preserve', 'react-native', or 'react'
    "declaration": true, // 生成相应的 '.d.ts' 文件
    "sourceMap": true, // 生成相应的 '.map' 文件
    "outFile": "./", // 将输出文件合并为一个文件
    "outDir": "./", // 指定输出目录
    "rootDir": "./", // 用来控制输出目录结构 --outDir.
    "removeComments": true, // 删除编译后的所有的注释
    "noEmit": true, // 不生成输出文件
    "importHelpers": true, // 从 tslib 导入辅助工具函数
    "isolatedModules": true, // 将每个文件做为单独的模块 (与 'ts.transpileModule' 类似).

    /* 严格的类型检查选项 */
    "strict": true, // 启用所有严格类型检查选项
    "noImplicitAny": true, // 在表达式和声明上有隐含的 any类型时报错
    "strictNullChecks": true, // 启用严格的 null 检查
    "noImplicitThis": true, // 当 this 表达式值为 any 类型的时候,生成一个错误
    "alwaysStrict": true, // 以严格模式检查每个模块,并在每个文件里加入 'use strict'

    /* 额外的检查 */
    "noUnusedLocals": true, // 有未使用的变量时,抛出错误
    "noUnusedParameters": true, // 有未使用的参数时,抛出错误
    "noImplicitReturns": true, // 并不是所有函数里的代码都有返回值时,抛出错误
    "noFallthroughCasesInSwitch": true, // 报告 switch 语句的 fallthrough 错误。(即,不允许 switch 的 case 语句贯穿)

    /* 模块解析选项 */
    "moduleResolution": "node", // 选择模块解析策略: 'node' (Node.js) or 'classic' (TypeScript pre-1.6)
    "baseUrl": "./", // 用于解析非相对模块名称的基目录
    "paths": {}, // 模块名到基于 baseUrl 的路径映射的列表
    "rootDirs": [], // 根文件夹列表,其组合内容表示项目运行时的结构内容
    "typeRoots": [], // 包含类型声明的文件列表
    "types": [], // 需要包含的类型声明文件名列表
    "allowSyntheticDefaultImports": true, // 允许从没有设置默认导出的模块中默认导入。

    /* Source Map Options */
    "sourceRoot": "./", // 指定调试器应该找到 TypeScript 文件而不是源文件的位置
    "mapRoot": "./", // 指定调试器应该找到映射文件而不是生成文件的位置
    "inlineSourceMap": true, // 生成单个 soucemaps 文件,而不是将 sourcemaps 生成不同的文件
    "inlineSources": true, // 将代码与 sourcemaps 生成到一个文件中,要求同时设置了 --inlineSourceMap 或 --sourceMap 属性

    /* 其他选项 */
    "experimentalDecorators": true, // 启用装饰器
    "emitDecoratorMetadata": true // 为装饰器提供元数据的支持
  }
}

# 装饰器

装饰器是什么?

  • 它是一个表达式
  • 该表达式被执行后,返回一个函数
  • 函数的入参分别为 target、name 和 descriptor
  • 执行该函数后,可能返回 descriptor 对象,用于配置 target 对象

# 类装饰器

function Gretter(target: Function): void {
    target.prototype.greet = function():void {
        console.log('hello,类装饰器')
    }
}
// 这里默认传递的参数就是 Gretting 类
@Gretter
class Gretting {
    constructor() {
        // 内部实现

}

let myGreeting = new Gretting();
(myGreeting as any).greet();

// 自定义输出问候语
function Greeter(greeting: string) {
  return function (target: Function) {
    target.prototype.greet = function (): void {
      console.log(greeting);
    };
  };
}

@Greeter("Hello TS!")
class Greeting {
  constructor() {
    // 内部实现
  }
}

let myGreeting = new Greeting();
(myGreeting as any).greet(); // console output: 'Hello TS!';

# 属性装饰器

function logProperty(target: any, key: string) {
  delete target[key];

  const backingField = "_" + key;

  Object.defineProperty(target, backingField, {
    writable: true,
    enumerable: true,
    configurable: true
  });

  // property getter
  const getter = function(this: any) {
    const currVal = this[backingField];
    console.log(`Get: ${key} => ${currVal}`);
    return currVal;
  };

  // property setter
  const setter = function(this: any, newVal: any) {
    console.log(`Set: ${key} => ${newVal}`);
    this[backingField] = newVal;
  };

  // Create new property with getter and setter
  Object.defineProperty(target, key, {
    get: getter,
    set: setter,
    enumerable: true,
    configurable: true
  });
}

class Person {
  @logProperty
  public name: string;

  constructor(name: string) {
    this.name = name;
  }
}

const p1 = new Person("semlinker");
p1.name = "kakuqo";

// 输出
Set: name => semlinker;
Set: name => kakuqo;

# 方法装饰器

function log(target: Object, propertyKey: string, descriptor: PropertyDescriptor) {
  let originalMethod = descriptor.value;
  descriptor.value = function(...args: any[]) {
    console.log("wrapped function: before invoking " + propertyKey);
    let result = originalMethod.apply(this, args);
    console.log("wrapped function: after invoking " + propertyKey);
    return result;
  };
}

class Task {
  @log
  runTask(arg: any): any {
    console.log("runTask invoked, args: " + arg);
    return "finished";
  }
}

let task = new Task();
let result = task.runTask("learn ts");
console.log("result: " + result);
打印结果
"wrapped function: before invoking runTask"
"runTask invoked, args: learn ts"
"wrapped function: after invoking runTask"
"result: finished"

# 参数装饰器

function Log(target: Function, key: string, parameterIndex: number) {
  let functionLogged = key || target.prototype.constructor.name;
  console.log(`The parameter in position ${parameterIndex} at ${functionLogged} has
	been decorated`);
}

class Greeter {
  greeting: string;
  constructor(@Log phrase: string) {
    this.greeting = phrase;
  }
}
"The parameter in position 0 at Greeter has been decorated"

# 内置对象

let b: Boolean = new Boolean(1);
let e: Error = new Error("Error occurred");
let d: Date = new Date();
let r: RegExp = /[a-z]/;

let body: HTMLElement = document.body;
let allDiv: NodeList = document.querySelectorAll("div");
document.addEventListener("click", function(e: MouseEvent) {
  // Do something
});

# 声明文件

文档地址 (opens new window)

# 高级进阶

# keyof

索引查询:对应任何类型T,keyof T的结果为该类型上所有共有属性key的联合:

interface Eg1 {
  name: string,
  readonly age: number,
}
// T1的类型实则是name | age
type T1 = keyof Eg1

class Eg2 {
  private name: string;
  public readonly age: number;
  protected home: string;
}
// T2实则被约束为 age
// 而name和home不是公有属性,所以不能被keyof获取到
type T2 = keyof Eg2

T[K] 索引访问

interface Eg1 {
  name: string,
  readonly age: number,
}
// string
type V1 = Eg1['name']
// string | number
type V2 = Eg1['name' | 'age']
// any
type V2 = Eg1['name' | 'age2222']
// string | number
type V3 = Eg1[keyof Eg1]

# partial

Partial<T>T的所有属性变成可选的。

/**
 * 主要通过K extends keyof T约束K必须为keyof T的子类型
 * keyof T得到的是T的所有key组成的联合类型
 */
type PartialOptional<T, K extends keyof T> = {
  [P in K]?: T[P];
}

/**
 * @example
 *     type Eg1 = { key1?: string; key2?: number }
 */
type Eg1 = PartialOptional<{
  key1: string,
  key2: number,
  key3: ''
}, 'key1' | 'key2'>;

# ReturnType

返回函数的返回值类型

export type UseBroswerLocationReturn = ReturnType<typeof useBrowserLocation>;

# Paramters

传入泛型函数类型,返回函数的参数类型

// 传入request 函数类型,然后用Paramters获取函数参数类型,然后当成接口去定义另一个函数的参数
return (...[url, config]: Parameters<typeof request>) =>
    request(url, { ...config, token: user?.token });

# Utilify type

Utilify type的作用就是传入泛型类型,然后对这个类型进行某种操作

常见的 Utilify Type

example中出现两次定义,后者代表解答或者类型

# Paramters

提取T类型的属性,作为一个新的类型

declare function f1(arg: { a: number; b: string }): void;
 
type T0 = Parameters<() => string>;
     
type T0 = []
type T1 = Parameters<(s: string) => void>;
     
type T1 = [s: string]
type T2 = Parameters<<T>(arg: T) => T>;
     
type T2 = [arg: unknown]
type T3 = Parameters<typeof f1>;
     
type T3 = [arg: {
    a: number;
    b: string;
}]
type T4 = Parameters<any>;
     
type T4 = unknown[]
type T5 = Parameters<never>;
     
type T5 = never
type T6 = Parameters<string>;
Type 'string' does not satisfy the constraint '(...args: any) => any'.
     
type T6 = never
type T7 = Parameters<Function>;
Type 'Function' does not satisfy the constraint '(...args: any) => any'.
  Type 'Function' provides no match for the signature '(...args: any): any'.
     
type T7 = never

# Partial

所有属性变为可选

/**
 * 主要通过K extends keyof T约束K必须为keyof T的子类型
 * keyof T得到的是T的所有key组成的联合类型
 */
type PartialOptional<T, K extends keyof T> = {
  [P in K]?: T[P];
}

/**
 * @example
 *     type Eg1 = { key1?: string; key2?: number }
 */
type Eg1 = PartialOptional<{
  key1: string,
  key2: number,
  key3: ''
}, 'key1' | 'key2'>;

# Required

所有属性变为必选

interface Props {
  a?: number;
  b?: string;
}
 
const obj: Props = { a: 5 };
 
const obj2: Required<Props> = { a: 5 };
// Property 'b' is missing in type '{ a: number; }' but required in type 'Required<Props>'.

# Readonly

只读所有属性

interface Todo {
  title: string;
}
 
const todo: Readonly<Todo> = {
  title: "Delete inactive users",
};
 
todo.title = "Hello";
// Cannot assign to 'title' because it is a read-only property.

# Record<K,T>

构造一个类型,该类型具有一组属性K,每个属性的类型为T。可用于将一个类型的属性映射为另一个类型。可以理解为定义一个限制key ,value 类型的新类型

type TodoProperty = "title" | "description";

type Todo = Record<TodoProperty, string>;

const todo: Todo = {
  title: '12',
  description: '12'
};

interface CatInfo {
  age: number;
  breed: string;
}
 
type CatName = "miffy" | "boris" | "mordred";
 
const cats: Record<CatName, CatInfo> = {
  miffy: { age: 10, breed: "Persian" },
  boris: { age: 5, breed: "Maine Coon" },
  mordred: { age: 16, breed: "British Shorthair" },
};
 
cats.boris;

# Pick<T,K>

通过在T中抽取一组属性K构建一个新类型.

interface Todo {
  title: string;
  description: string;
  done: boolean;
}

type TodoBase = Pick<Todo, 'title' | 'done'>;

const todo: TodoBase = {
  title: 'First Todo',
  done: false
};

# Omit<T,K>

通过在T中抽取除去属性K,构建一个新类型

interface Todo {
  title: string;
  description: string;
  done: boolean;
}

type TodoBase = Omit<Todo, 'description'>;

const todo: TodoBase = {
  title: 'First Todo',
  done: false
};

# Exclude<T,U>

从T中排除可分配给U的属性,剩余的属性构成新的类型

type T0 = Exclude<string | number | boolean, boolean > // string | number

type T0 = Exclude<"a" | "b" | "c", "a">;
     
type T0 = "b" | "c"
type T1 = Exclude<"a" | "b" | "c", "a" | "b">;
     
type T1 = "c"
type T2 = Exclude<string | number | (() => void), Function>;
     
type T2 = string | number

# Extract<T,U>

从 T 中取出可以分配给 U 的属性,组成新的类型

type T0 = Extract<"a" | "b" | "c", "a" | "f">;
     
type T0 = "a"
type T1 = Extract<string | number | (() => void), Function>;
     
type T1 = () => void

# ReturnType

T函数类型的返回类型

declare function f1(): { a: number; b: string };
 
type T0 = ReturnType<() => string>;
     
type T0 = string
type T1 = ReturnType<(s: string) => void>;
     
type T1 = void
type T2 = ReturnType<<T>() => T>;
     
type T2 = unknown
type T3 = ReturnType<<T extends U, U extends number[]>() => T>;
     
type T3 = number[]
type T4 = ReturnType<typeof f1>;
     
type T4 = {
    a: number;
    b: string;
}
type T5 = ReturnType<any>;
     
type T5 = any
type T6 = ReturnType<never>;
     
type T6 = never

# TS 解决空值或者undefined问题

  1. loadsh 解决

    // data.courseInfo.description 完美解决null或者undefined问题
    let a = _.get(data,'courseInfo.description','this is a description') //第三个参数是默认值,也就是当路径存在undefined就会返回这个默认值
    
  2. 可选操作符 ?

# TS重载技巧

例: useAxios(url,config,instance) 支持传入三个参数,其中第二个和第三个参数可选

那么定义函数就可以先定义方法重载,包含所有情况

export function useAxios<T = any>(url: string, config?: AxiosRequestConfig): UseAxiosReturn<T>
export function useAxios<T = any>(url: string, instance?: AxiosInstance): UseAxiosReturn<T>
export function useAxios<T = any>(url: string, config: AxiosRequestConfig, instance: AxiosInstance): UseAxiosReturn<T>
  
// 真正实现该函数
export function useAxios<T = any>(url: string, ...args: any[]) { 

}
Last update: 3/6/2022, 9:03:36 AM