TypeScript基础

TS编译运行

ts不是在终端运行,是一门中间语言,最终编译为js运行。

手动编译

// 1. ts编译为js
npm i -g typescript
// 查看版本
tsc -v
// 编译运行
tsc .\test.ts
// 2. ts直接在node环境下运行
npm i -g ts-node
// 查看版本
ts-node -v
// 编译运行
ts-node .\test.ts

webpack构建ts项目

在这里插入图片描述

安装

# 生成package.json文件
npm init -y

# 安装webpack环境
npm i -D webpack webpack-cli

# 安装ts解析
npm i -D ts-loader

# 安装html模板
npm i -D html-webpack-plugin

# 安装webpack本地服务器,热更新
npm i -D webpack-dev-server
# 简写
npm i -D webpack webpack-cli ts-loader html-webpack-plugin webpack-dev-server

webpack.config.js

在根目录下编写webpack.config.js文件

const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");

module.exports = {
  mode: "development",
  entry: "./src/index.ts",
  output: {
    path: path.resolve(__dirname, "./dist"),
    filename: "bundle.js",
  },
  resolve: {
    extensions: [".ts", ".js", ".cjs", ".json"],
  },
  devServer: {
    port: 1988,
    proxy: {},
  },
  module: {
    rules: [
      {
        test: /\.ts$/,
        loader: "ts-loader",
      },
      {
        test: /\.(png|svg|jpg|jpeg|gif)$/i,
        type: "asset/resource",
      },
    ],
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: "./public/index.html",
    }),
  ],
};

tsconfig.json

生成ts配置文件,tsconfig.json

tsc --init
1. tsconfig.json文件所在目录即为TS项目的根目录
2. 指定该根目录下的ts文件编译方式
3. webpack中使用ts-loader打包时,自动读取tsconfig配置文件来编译ts代码
~~~json
"compilerOptions": {
  "incremental": true, // TS编译器在第一次编译之后会生成一个存储编译信息的文件,第二次编译会在第一次的基础上进行增量编译,可以提高编译的速度
  "tsBuildInfoFile": "./buildFile", // 增量编译文件的存储位置
  "diagnostics": true, // 打印诊断信息 
  "target": "ES5", // 编译目标语言的版本
  "module": "CommonJS", // 模块化使用的标准
  "outFile": "./app.js", // 将多个相互依赖的文件生成一个文件,可以用在AMD模块中,即开启时应设置"module": "AMD",
  "lib": ["DOM", "ES2015", "ScriptHost", "ES2019.Array"], // TS需要引用的库,即声明文件,es5 默认引用dom、es5、scripthost,如需要使用es的高级版本特性,通常都需要配置,如es8的数组新特性需要引入"ES2019.Array",
  "allowJS": true, // 允许编译器编译JS,JSX文件
  "checkJs": true, // 允许在JS文件中报错,通常与allowJS一起使用
  "outDir": "./dist", // 指定输出目录
  "rootDir": "./", // 指定输出文件目录(用于输出),用于控制输出目录结构
  "declaration": true, // 生成声明文件,开启后会自动生成声明文件
  "declarationDir": "./file", // 指定生成声明文件存放目录
  "emitDeclarationOnly": true, // 只生成声明文件,而不会生成js文件
  "sourceMap": true, // 生成目标文件的sourceMap文件
  "inlineSourceMap": true, // 生成目标文件的inline SourceMap,inline SourceMap会包含在生成的js文件中
  "declarationMap": true, // 为声明文件生成sourceMap
  "typeRoots": [], // 第三方声明文件的目录,默认时node_modules/@types
  "types": [], // 加载的声明文件包
  "removeComments":true, // 删除注释 
  "noEmit": true, // 不输出文件,即编译后不会生成任何js文件
  "noEmitOnError": true, // 发送错误时不输出任何文件
  "noEmitHelpers": true, // 不生成helper函数,减小体积,需要额外安装,常配合importHelpers一起使用
  "importHelpers": true, // 通过tslib引入helper函数,文件必须是模块
  "downlevelIteration": true, // 降级遍历器实现,如果目标源是es3/5,那么遍历器会有降级的实现
  "strict": true, // 开启所有严格的类型检查
  "alwaysStrict": true, // 在代码中注入'use strict'
  "noImplicitAny": true, // 不允许隐式的any类型
  "strictNullChecks": true, // 不允许把null、undefined赋值给其他类型的变量
  "strictFunctionTypes": true, // 不允许函数参数双向协变
  "strictPropertyInitialization": true, // 类的实例属性必须初始化
  "strictBindCallApply": true, // 严格的bind/call/apply检查
  "noImplicitThis": true, // 不允许this有隐式的any类型
  "noUnusedLocals": true, // 检查只声明、未使用的局部变量(只提示不报错)
  "noUnusedParameters": true, // 检查未使用的函数参数(只提示不报错)
  "noFallthroughCasesInSwitch": true, // 防止switch语句贯穿(即如果没有break语句后面不会执行)
  "noImplicitReturns": true, //每个分支都会有返回值
  "esModuleInterop": true, // 允许export=导出,由import from 导入
  "allowUmdGlobalAccess": true, // 允许在模块中全局变量的方式访问umd模块
  "moduleResolution": "node", // 模块解析策略,ts默认用node的解析策略,即相对的方式导入
  "baseUrl": "./", // 解析非相对模块的基地址,默认是当前目录
  "paths": { // 路径映射,相对于baseUrl
    // 如使用jq时不想使用默认版本,而需要手动指定版本,可进行如下配置
    "jquery": ["node_modules/jquery/dist/jquery.min.js"]
  },
  "rootDirs": ["src","out"], // 将多个目录放在一个虚拟目录下,用于运行时,即编译后引入文件的位置可能发生变化,这也设置可以虚拟src和out在同一个目录下,不用再去改变路径也不会报错
  "listEmittedFiles": true, // 打印输出文件
  "listFiles": true// 打印编译的文件(包括引用的声明文件)
}
 
// 指定一个匹配列表(编译指定路径下的所有ts相关文件)
"include": [
   "src/**/*"
],
// 指定一个排除列表(不编译指定的ts文件)
 "exclude": [
   "demo.ts"
],
// 指定哪些文件使用该配置(编译指定文件,不能指定文件夹)
 "files": [
   "demo.ts"
]

基本类型

声明的类型称为类型注解(Type Annotation)

基本类型:
number、string、boolean、undefined、null、Symbol、BigInt

// 非引用类型:
// number、string、boolean、undefined、null、Symbol、BigInt
let num:number = 10
let num2:number = 0b110
let num3:number = NaN
let num4:number = Infinity

let flag:boolean = false

let str:string = '123'
let str2:string = "abc"

let unde:undefined = undefined

let nu:null = null

// 表示唯一,即使内容一样也表示唯一
let s1:symbol = Symbol('k1')
let s2:symbol = Symbol('k1')
let person = {
    [k1]:'v1',
    [k1]:'v2'
}

// 字面量类型
var ENV:"production"|"development"|"test"='development'
let age:99|100 = 99

枚举类型

enum nums {
  one = 1,
  two,
  three
}

// 从1开始递增
// 3
console.log(nums.three);
// 通过名称拿到值
// 也能从值拿到名称
// { '1': 'one', '2': 'two', '3': 'three', one: 1, two: 2, three: 3 }
console.log(nums);

引用类型

引用类型:object、array、function

数组

// 只能空数组
let arr1:[] = []
let arr2:number[] = [1,2,3]
let arr3:Array<number> = [1,2,3]
// 二维数组
let arr4:number[][]=[[1],[2]]
let arr5:Array<Array<number>>=[[1],[2]]

// 数组内有字符串和数字类型
var arr6:(string|number)[] = ['cjc',999]

// 字符串数组或数字数组
var arr7: string[] | number[] = ['a', 'b']
var arr8: string[] | number[] = [1, 2]

// 元祖,顺序和类型都要满足
// 最后任意类型且可有可无
var arr9: [string, number, any?] = ['cjc', 999, true]
// 二维数组
var arr10: [number,string][] = [
	[0,'a'],
	[1,'b']
]

// 不定参数
var arr11: [string, number, ...(number|string)[]] = ['cjc', 999, 1,2,3]

对象

// 空对象
let obj:object = {}
let obj2:{} = {name:'cjc'}
let obj3:{name:string,age:number} = {name:'cjc',age:999}

// 键名数字类型
var obj4: { [propNmae: number]: string } = { 1: 'a', 2: 'b' }

// 可选 ?
var obj5: { x: number, y?: boolean } = { x: 1 }

函数

函数定义方式

// 1.声明式函数定义
function sumFun(a: number, ...args: number[]): number {
  return a + args.reduce((preVal: number, curVal: number) => {
    return preVal + curVal
  })
}
console.log(sumFun(1, 2, 3)); //6

// 2.函数变量
let foo2: (a: string) => string = function (a) {
  return a
}

// 3.匿名函数
// 一般匿名函数不用指定类型
callback(function(){})

// this处理
function foo3(this:void,a:number){}
foo3(1)

函数类型

方式1 函数类型表达式

  • 定义函数类型若有参数,则必须携带形参名
// 定义函数的类型
// 必须携带形参名
type calcFnType = (num1: number, num2: number) => number

function calc(calcFn: calcFnType) {
  let res = calcFn(1, 2)
  return res
}

function add(a: number, b: number) {
  return a + b
}

function minus(a: number, b: number) {
  return a - b
}

let res = calc(add)
// 3
console.log(res);

let res2 = calc(minus)
// -1
console.log(res2);

方式2 调用签名(call signatures)
函数本身也是对象类型,可以给函数指定类型的同时添加属性

  • 定义函数类型若有参数,则必须携带形参名
  • 函数类型语法 (形参名:类型) : 类型
interface IFoo {
  name: string,
  age: number,
  (num: number): number
}

const foo: IFoo = (num: number): number => {
  return 1
}

foo.name = 'ccc'
foo.age = 999

函数重载

函数重载:函数名相同,形参不同(个数、类型、顺序)

//输入数字 `123` 的时候,输出反转的数字 `321`,输入字符串 `'hello'` 的时候,输出反转的字符串 `'olleh'`

// 1.函数重载签名
function reverse(x: number): number;
function reverse(x: string): string;

// 2.通用函数实现
function reverse(x: any): any {
    if (typeof x === 'number') {
        return Number(x.toString().split('').reverse().join(''));
    } else if (typeof x === 'string') {
        return x.split('').reverse().join('');
    }
}

any、unknown

any、unknown都能被赋予任何类型的值

any

  1. any能赋值给任何类型
  2. any类型进行任何操作

unkonwn

  1. unknown 类型只能赋值给 any 类型和 unknown 类型本身
  2. unknown类型不能进行任何操作,类型缩小(例如typeof)后才能进行对应类型的操作
// 1.any
let a: any = 123
let b: string = a

// 2.unknown
// unknown比any更安全,unknown类型只能赋值给unknown或any
let a2: unknown = 123
//不能将类型“unknown”分配给类型“string”
// let b2:string = a2
let b2: any = a2

void

函数没有返回值时,返回void类型

type execFunType = (...args: any[]) => void
function delayFun(fn: execFunType) {
  setTimeout(() => {
    fn('ccc', 999)
  }, 1000);
}

delayFun((name, age) => {
  console.log(name);
  console.log(age);
})

never

never类型表示永不存在的值的类型
应用场景:

  1. 函数中总是抛出异常或死循环(不会有返回值)
  2. 封装框架或工具时,确保所有类型都有对应的实现
// 返回never的函数必须存在无法达到的终点
function error(message: string): never {
  throw new Error(message);
}

function infiniteLoop(): never {
  while (true) {}
}

没有对boolean类型的处理,报错:
在这里插入图片描述
增加对boolean的处理:

function foo(msg: string | number | boolean) {
  switch (typeof msg) {
    case 'string':
      console.log(msg.length);
      break;
    case "number":
      console.log(msg);
      break;
    case "boolean":
      break;
    default:
      // 此时msg收窄为never
      const check: never = msg
      break;
  }
}

tuple元祖

一般数组中存放相同的数据类型,元祖中可以存放不同的数据类型

const stu:[string,number] = ['CCC',999]
const name = stu[2]

应用场景:用于函数的返回值

function useState<T>(state: T): [T, (newState: T) => void] {
  let currentState = state
  const changeState = (newState: T) => {
    currentState = newState
  }

  return [currentState, changeState]
}

//const counter: number
//const setState: (newState: number) => void
const [counter, setState] = useState(10)
// 10
console.log(counter);

字面量类型

function request(url: string, method: "get" | "post") { }

const info1 = {
  url: 'xxx',
  method: "get"
}
// info1中method的类型为string
// 方式1 断言为字面量"get"
request(info1.url, info1.method as "get")


// 方式2 将info对象类型断言为字面量类型  
// 字面量推理
// const info2: { url: string, method: "get" | "post" } = {
//   url: "xxx",
//   method: "get"
// }
const info2 = {
  url: "xxx",
  method: "get"
} as const

request(info2.url, info2.method)

联合类型、交叉类型

联合类型

const a:number|string|boolean = true

交叉类型

interface IPerson {
  name: string,
  age: number
}

interface IStu extends IPerson {
  name: string
  study: () => void
}

// StuType类型具有IPerson和IStu的所有属性
type StuType = IPerson & IStu

const s1: StuType = {
  name: 'ccc',
  age: 999,
  study() {
    console.log(1);
  }
}

JS内置类型

JS有ECMA、DOM、BOM组成
接下来介绍JS内置API相关的类型

// ECMA类型
let date: Date = new Date();
let reg: RegExp = new RegExp("/d/");
let err: Error = new Error();
let xhr: XMLHttpRequest = new XMLHttpRequest();
let local: Storage = localStorage;
let cookie: string = document.cookie;
let promise: Promise<number> = new Promise((resolve) => resolve(1));

// DOM元素类型
// 1.HTML[元素名]Element
// 2.HTMLElement
// 3.断言为Element
// 4.NodeListOf<DOM类型>
let div: HTMLDivElement | null = document.querySelector("div");
let h1: HTMLHeadingElement | null = document.querySelector("h1");
let header: HTMLElement | null = document.querySelector("header");
let span = document.querySelector("span") as Element;
let lis: NodeListOf<HTMLLIElement> = document.querySelectorAll("li");

type、interface

描述对象、函数

type类型别名和interface接口都可以描述对象和函数

type

// 描述对象
type IPoint = {
  x: number
  y: number
}
const p1: IPoint = { x: 1, y: 1 }
console.log(p1); //{ x: 1, y: 1 }


// 描述函数
type IGetPoint = (x: number, y: number) => ({ x: number, y: number })
const getPoint: IGetPoint = function (x: number, y: number): ({ x: number, y: number }) {
  return ({ x, y })
}

const p2 = getPoint(2, 2)
console.log(p2); //{ x: 2, y: 2 }

interface

// 描述对象
interface IPoint {
  x: number
  y: number
}
const p1: IPoint = { x: 1, y: 1 }
console.log(p1); //{ x: 1, y: 1 }


// 描述函数
interface IGetPoint {
  (x: number, y: number): ({ x: number, y: number })
}
const getPoint: IGetPoint = function (x: number, y: number): ({ x: number, y: number }) {
  return ({ x, y })
}
const p2 = getPoint(2, 2)
console.log(p2); //{ x: 2, y: 2 }

type 还可以为基本类型起别名,interface不能

// primitive
type Name = string;

// union
type PartialPoint = PartialPointX | PartialPointY;

// tuple
type Data = [number, string];

可选、只读、索引签名

// 定义对象接口
interface Person {
  // 1.只读属性
  readonly id: number,
  name: string,
  age: number,
  // 2.可选属性
  gender?: string,
  // 3.动态属性名
  [attrName: string]: any
}

let p1: Person = {
  id: 1,
  name: 'cjc',
  age: 999,
  // 任意属性
  song: 'jinitaimei'
}

索引签名

interface ICollection {
  [index: number]: any
  length: number
}

function printCollection(collection: ICollection) {
  for (let i = 0; i < collection.length; i++) {
    console.log(collection[i]);
  }
}

const arr = [1, 2, 3]
printCollection(arr)

const str = 'abc'
printCollection(str)

类型扩展(继承)

接口可以扩展类型别名,而反过来是不行的

Interface extends interface

interface PartialPointX { x: number; }
interface Point extends PartialPointX { 
  y: number; 
}

Type alias extends type alias

type PartialPointX = { x: number; };
type Point = PartialPointX & { y: number; };

Interface extends type alias

type PartialPointX = { x: number; };
interface Point extends PartialPointX { y: number; }

Type alias extends interface

interface PartialPointX { x: number; }
type Point = PartialPointX & { y: number; };

声明合并

同一个接口声明会合并
同一个类型别名会报错

interface pointType {
  x: number,
  y: number
}

interface pointType {
  z?: number
}

const p1: pointType = {
  x: 1,
  y: 2,
}

const p2: pointType = {
  x: 1,
  y: 2,
  z: 3
}

//{ x: 1, y: 2 } object
console.log(p1, typeof p1);
//{ x: 1, y: 2, z: 3 } object
console.log(p2, typeof p2);

类型断言、非空类型断言

当TS没法获取某个确定类型时,使用类型断言(Type Assertions)

// let div: HTMLDivElement | null
let div = document.querySelector('div')

// 当用类获取元素时,TS无法推断是什么类型
// let box1: Element | null
let box1 = document.querySelector('.box')

// let box2: HTMLDivElement
let box2 = document.querySelector('.box') as HTMLDivElement

断言能断言成更具体的类型或不太具体的类型

// const age: number
const age: number = 99
// const age2: any
const age2 = age as any
// const age3: string
const age3 = age2 as string

非空断言


interface IPerson {
  name: string,
  age: number,
  hobbies?: string[]
}

const p1: IPerson = {
  name: 'ccc',
  age: 999,
  hobbies: ['a']
}

// 访问属性 可选连 ?
console.log('设置之后:', p1.hobbies?.[0]);

// 设置属性
// 方式1 类型缩小
if (p1.hobbies) {
  p1.hobbies[0] = 'rap'
}

// 方式2 非空类型断言
p1.hobbies![0] = 'rap'

console.log('设置之后:', p1.hobbies?.[0]);

类型缩小

  • typeof
  • ===!== == != 常用于字面量类型的判断
  • instanceof
  • in

TS面向对象

class语法

成员修饰符

  • public 公有成员任何地方可见,默认值
  • protected 在类内部及子类中可见
  • private 私有成员,在类内部可见

属性修饰符

  • readonly 只读属性
  • ? 可选属性
interface IPerson {
  name: string;
  age: number;
}

class Person implements IPerson {
  name: string;
  age: number;
  constructor(name: string, age: number) {
    this.name = name;
    this.age = age;
  }
}

type gradeItemType = {
  math: number;
  chinese: number;
};
class Student extends Person {
  // 1.实例属性,实例方法
  // 修饰符readonly、private、protected、public
  gender: boolean;
  private _secretMsg?: string;

  constructor(name: string, age: number, gender: boolean) {
    super(name, age);
    this.gender = gender;
  }

  // 2.静态属性,静态方法
  // 静态方法中this只能调用静态修饰符的属性和方法
  static grade: gradeItemType[];
  static getGrade() {
    return this.grade;
  }

  // 3.get,set
  get secretMsg() {
    return "get secretMsg:" + this._secretMsg;
  }

  set secretMsg(newVal) {
    this._secretMsg = "set secretMsg:" + newVal;
  }
}

// 1.实例属性,方法
let stu1 = new Student("ccc", 999, true);
console.log(stu1.gender);

// 2.类调用静态方法,静态属性
Student.grade = [
  { math: 100, chinese: 100 },
  { math: 99, chinese: 99 },
];
console.log(Student.getGrade());

// 3.get,set
stu1.secretMsg = "哈哈哈";
console.log(stu1.secretMsg);

构造函数语法糖

class Person {
  public name
  public age
  constructor(name: string, age: number) {
    this.name = name
    this.age = age
  }
}

// 以上代码的语法糖形式:
class Person2 {
  constructor(public name: string, public age: number) { }
}

泛型

基本使用

function foo<T, E>(a: T, b: E) { }

// 1.自动类型推断
foo(1, { name: 'ccc', age: 999 })

// 2. 类型注解
foo<number, { name: string, age: number }>(1, { name: 'ccc', age: 999 })

泛型约束

确保某属性存在(extends)

T extends U

  • T 能够赋值给 U(T 是 U的子集)

例:传入的类型必须有length属性

//T必须有length属性,且length属性为number类型
function longer<T extends { length: number }>(a: T, b: T) {
  return a.length > b.length ? a : b
}

// const longerArray: number[]
const longerArray = longer([1, 2], [1, 2, 3]);
// const longerString: "alice" | "bob"
const longerString = longer("alice", "bob");
// 类型“number”的参数不能赋给类型“{ length: number; }”的参数。
const notOK = longer(10, 100);

检查对象上的键是否存在(keyof)

例:根据对象的键(不管是否存在),获取对象的值

// function getProperty(obj: any, key: any): any
function getProperty(obj, key) {
  return obj[key]
}

const p = {
  name: 'ccc',
  age: 99
}

// ccc
console.log(getProperty(p, "name"));

// 若没有类型约束,可以传入p中不存在的键
// undefined
console.log(getProperty(p, "gender"));

例:根据对象的键(只能获取对象p中存在的键),获取对象的值
keyof 获取p中所有的键组成的联合类型,如string|nunmber

// keyof使用
interface Person {
  name: string;
  age: number;
}

type K1 = keyof Person; // "name" | "age"
type K2 = keyof Person[]; // number | "length" | "push" | "concat" | ...
// function getProperty<O, K extends keyof O>(obj: O, key: K): O[K]
function getProperty<O, K extends keyof O>(obj: O, key: K) {
  return obj[key]
}

const p = {
  name: 'ccc',
  age: 99
}

// ccc
console.log(getProperty(p, "name"));

// 类型“"gender"”的参数不能赋给类型“"name" | "age"”的参数
// console.log(getProperty(p, "gender"));

泛型默认类型

interface MyObject<T = string> {
  id: T;
}

const numObject: MyObject = { id: "abc" };
const strObject: MyObject<number> = { id: 123 };
  1. 有默认类型的类型参数被认为是可选的。
  2. 必选的类型参数不能在可选的类型参数后。

映射类型

  1. 拷贝传入类型T的所有键值对到另一个类型
  2. 使用type定义映射类型,不能使用interface定义
  3. 拷贝过程中能对类型进行操作
interface IPerson {
  name: string
  age: number
}

type MapType<T> = {
  // 拷贝传入类型T的所有键值对
  [key in keyof T]: T[key]
}

type NewPerson = MapType<IPerson>

let p1: NewPerson = { name: 'ccc', age: 999 }

例子:将所有类型转为必填

  • 在修饰符之前可以加上减号-,表示去除该修饰符
  • 在修饰符之前可以加上加号+,默认省略
interface IPerson {
  name: string
  age?: number
  readonly gender: string
}

type mapType<T> = {
  // 拷贝传入类型T的所有键值对
  // - 去除修饰符
  -readonly [key in keyof T]-?: T[key]
}

type NewPerson = mapType<IPerson>

let p1: NewPerson = { name: 'ccc', age: 999, gender: 'male' }

ts模块化

没有export的js或ts文件会被认为是脚本。脚本中声明的变量和类型等在全局作用域中。

将脚本变为模块 export {} (即使没有导出任何内容)

导出类型
在这里插入图片描述
导入类型
在这里插入图片描述
在这里插入图片描述

ts声明文件

xxx.d.ts称为类型声明文件(Type Declaration)或类型定义文件(Type Definition)

ts内置类型声明文件

在这里插入图片描述

第三方库类型声明文件

  1. axios等第三方库包含自己编写的 xx.d.ts类型声明文件
  2. react等不含有类型声明文件,则需要另外导入类型声明库
npm i react
npm i -S @types/react

在这里插入图片描述

自定义类型声明文件

自定义类型声明文件 xxx.d.ts

// 声明变量类型
declare const name:string
declare const age:number

// 声明函数类型
declare function foo(a:number):number

// 声明类的类型
declare class Person {
	constructor(public name:string,public age:number)
}

// 声明模块
// 假设lodash没有类型库
declare module "lodash" {
	export function join(args: any[]):string;
}

// 声明命名空间
// jQuery等使用CDN方式使用时(不是引入模块的方式)
declare namespace $ {
	export function ajax(setting:any):any
}

例:ts中导入图片文件,报错。需要声明文件模块
在这里插入图片描述
在类型声明文件中:

declare module "*.avif"

条件类型

条件类型在函数重载中应用

条件类型(Conditional Types)在函数重载中的应用

function sum(arg1: number, arg2: number): number
function sum(arg1: string, arg2: string): string

function sum(arg1, arg2) {
  return arg1 + arg2
}

//function sum(arg1: number, arg2: number): number (+1 overload)
sum(1, 2)
//function sum(arg1: string, arg2: string): string (+1 overload)
sum('a', 'b')
function sum<T>(arg1: T, arg2: T): T extends number ? number : string

function sum(arg1, arg2) {
  return arg1 + arg2
}

//function sum<number>(arg1: number, arg2: number): number
sum(1, 2)
//function sum<string>(arg1: string, arg2: string): string
sum('a', 'b')

在条件类型中推断类型(infer)

在条件类型中推断(Inferring Within Conditional Types)

  1. infer关键字只能在extends后面语句中使用
  2. infer推断的变量U只能在true分支中使用

例1:封装获取数组中元素类型的工具

// T是数组类型则返回数组中元素的类型
type UnpackedArray<T> = T extends (infer U)[] ? U : T

const arr1 = [1, 2, 3]
//type typeElem = number
type typeElem1 = UnpackedArray<typeof arr1>

const arr2 = ['a', 'b']
//type typeElem2 = string
type typeElem2 = UnpackedArray<typeof arr2>

例2:封装获取函数返回值类型的工具

type fnType = (arg1: number, arg2: number) => number

function foo() {
  return "hi"
}

// 定义判断函数返回值类型的工具
type MyReturnType<T extends (...args: any) => any> = T extends (...args: any) => infer R ? R : never

//type fnReturnType = number
type fnReturnType = MyReturnType<fnType>
// type fooReturnType = string
type fooReturnType = MyReturnType<typeof foo>

例3:封装获取函数参数类型的工具

type fnType = (arg1: number, arg2: number) => number

function foo() {
  return "hi"
}

// 定义判断函数返回值类型的工具
type MyParamType<T extends (...args: any) => any> = T extends (...args: infer A) => any ? A : never

//type fnReturnType = [arg1: number, arg2: number]
type fnReturnType = MyParamType<fnType>
//type fooReturnType = []
type fooReturnType = MyParamType<typeof foo>

例4:获取数组第一个元素类型

type firstType<T> = T extends [infer oneType, ...any[]]
  ? oneType
  : T;

分发条件类型

分发条件类型(Distributive Conditional Types)
在泛型中传入联合类型,联合类型的每一项会分发到条件类型中判断

type toArray<T> = T extends any ? T[] : never

//type newType = string[] | number[]
type newType = toArray<string | number>

内置工具类型

Partial

将类型对象的所有属性变为可选

interface IPerson {
  name: string,
  age: number,
  gender?: boolean
}

// type IPersonOptional = {
//   name?: string | undefined;
//   age?: number | undefined;
//   gender?: boolean | undefined;
// }
type IPersonOptional = Partial<IPerson>

自定义实现:

type MyPartial<T> = {
  [k in keyof T]?: T[k]
}

Required

将类型对象的所有属性变为必选

interface IPerson {
  name: string,
  age?: number,
  gender?: boolean
}

// type IPersonRequire = {
//   name: string;
//   age: number;
//   gender: boolean;
// }
type IPersonRequire = Required<IPerson>

自定义实现:

type MyRequired<T> = {
  [k in keyof T]-?: T[k]
}

Readonly

将类型对象的所有属性变为只读

interface IPerson {
  name: string,
  age?: number,
  gender?: boolean
}

// type IPersonRequire = {
//   readonly name: string;
//   readonly age?: number | undefined;
//   readonly gender?: boolean | undefined;
// }
type IPersonRequire = Readonly<IPerson>

自定义实现:

type MyReadonly<T> = {
  readonly [k in keyof T]: T[k]
}

Record<Keys,Type>

构造一个类型对象,key为Keys类型,value为Type类型

interface IPerson {
  name: string,
  age?: number,
  gender?: boolean
}

// 字面量类型
type no = "01" | "02"

// 构造一个类型对象,key为no类型,value为IPerson类型
type StusType = Record<no, IPerson>

const stus: StusType = {
  "01": {
    name: 'a'
  },
  "02": {
    name: 'b'
  }
}
type MyRecord<K extends keyof any, T> = {
  [P in K]: T
}

Pick<Type,Keys>

构造一个类型,从Type类型中挑选一些属性Keys

interface IPerson {
  name: string,
  age?: number,
  gender?: boolean
}

// type nameType = {
//   name: string;
// }
type nameType = Pick<IPerson, 'name'>

// type nameAgeType = {
//   name: string;
//   age?: number | undefined;
// }
type nameAgeType = Pick<IPerson, 'name' | 'age'>
type MyPick<T, K extends keyof T> = {
  [P in K]: T[P]
}

Omit<Type,Keys>

构造一个类型,从Type类型中删掉一些属性Keys

interface IPerson {
  name: string,
  age?: number,
  gender?: boolean
}

// type nameType = {
//   name: string;
// }
type nameType = Omit<IPerson, 'gender' | 'age'>
type MyOmit<T, K extends any> = {
  [P in keyof T as P extends K ? never : P]: T[P]
}

Exclude<T,U>

构造一个类型,从联合类型T中删掉一些属性U

type nums = 1 | 2 | 3

// type oneType = 1
type oneType = MyExclude<nums, 2 | 3>
type MyExclude<T, U> = T extends U ? never : T

Extract<T,U>

构造一个类型,从联合类型T中取出一些属性U

type nums = 1 | 2 | 3

//type oneType = 1
type oneType = Extract<nums, 1>
// Extract from T those types that are assignable to U
type MyExtract<T, U> = T extends U ? T : never

NonNullable

构造一个类型,删除T中所有null或undefined

// Exclude null and undefined from T
type MyNonNullable<T> = T extends null | undefined ? never : T

type nums = 1 | 2 | 3 | null | undefined

// type numsType = 1 | 2 | 3
type numsType = NonNullable<nums>

ReturnType

函数返回值类型

type fnType = (arg1: number, arg2: number) => number

function foo() {
  return "hi"
}

// 定义判断函数返回值类型的工具
type MyReturnType<T extends (...args: any) => any> = T extends (...args: any) => infer R ? R : never

//type fnReturnType = number
type fnReturnType = MyReturnType<fnType>
// type fooReturnType = string
type fooReturnType = MyReturnType<typeof foo>

InstanceType

由T的构造函数的实例组成的类型

type MyInstanceType<T extends abstract new (...args: any) => any> = T extends abstract new (...args: any) => infer R ? R : never
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值