TypeScript(新手入门必备文章)

本文详细介绍了TypeScript的基础知识,包括数据类型如void、never、any、联合类型、数组、元组和枚举。深入探讨了接口(Interface)、函数、类以及泛型的使用。此外,还讲解了命名空间和装饰器的概念,包括类装饰器、属性装饰器、方法装饰器和参数装饰器的用法。文章最后提到了TypeScript中实用工具类型的应用以及接口与type的区别。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

初识TS

npm install -g typescript 下载ts到全局
tsc -v 查看版本
同级创建tsconfig.json 文件 用于ts的配置

cmd中执行tsc hello.ts 可以创建出 js
ts代码报错也可以生成js文件

const hello = (name: string) => {
  return `hello${name}`;
};
hello("liusheng");
// hello(123); 会报错,编译也会报错

// tsc -w class.ts 监听变化,生成js文件
// npm install -g ts-node 一步运行
// ts-node class.ts

tsc --init 生成配置文件

在线的TS演练场-尝试自己写一下

数据类型

基础类型

  • boolean
  • number
  • string
  • null和undefined
/* 
原始数据类型
*/
// let isDone: boolean = 123;
let isDone: boolean = false;

let age: number = 13;
let binaryNumber: number = 0b1111; //es6  二进制和八进制

let firstName: string = "haohao";
let message: string = `Hello,${firstName},age is ${age}`;

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

// let num: number = "string"; //报错
let num: number = undefined;

void类型

  • void类型像是与any类型相反,它表示没有任何类型。 当一个函数没有返回值时,你通常会见到其返回值类型是 void:
function warnUser(): void {
  console.log("This is my warning message");
}
warnUser();
// 注意:声明一个void类型的变量没有什么大用,因为你只能为它赋予undefined和null
let unusable: void = undefined;

never类型

  • 这个类型和 void 有些相似,也是不返回值,标示函数,比如:
    function error(): never {
      throw new Error("error");
    }
    error();
    
    // function endless(): never {
    //   while (true) {
    //     console.log(111);
    //   }
    // }
    // endless();
    
  • 通俗理解 void 与 never:
  • void 可以理解为一个没有 return 但能执行完毕的函数;
    never 是一个函数体无法结束执行的函数;

any类型

/* 
第三方,无法确定类型,any:宽泛的约束条件
*/
let notSure: any = "4";
notSure = `maybe it is a string`;
notSure = true;

notSure.myName;
notSure.getName();

// any类型 在.调用方法时没有提示
firstName.charCodeAt; //string 有提示
notSure.charCodeAt; //any  没提示

联合类型

/* 
联合类型  可以赋值几个类型
*/
let numberOrString: number | string = 5;
numberOrString = "55";
// numberOrString = true; //报错

在这里插入图片描述

数组

/* 
数组 
类型 + [] 表示
*/
// let arrOfNumber: number[] = [1, 2, "str", 4];//报错
let arrOfNumber: number[] = [1, 2, 3, 4];
arrOfNumber.push(5);
// arrOfNumber.push("str");//报错

类数组

/* 
类数组
*/
function test() {
  console.log(arguments); //IArguments
  // arguments.length
  // arguments[1]
  // arguments.forEach//报错,类数组找不到数组原型的方法
  //   let arr: any[] = arguments;//报错
}

元组

/* 
元组   tuple
*/
// 第一项  字符串 第二项 boolean  第三项  数字   用any就丧失了意义
// let user: [string, number] = ["string"];//数组里多参数,少参数也会报错
let user: [string, number] = ["string", 1];
// user = [1, "string"];//报错,类型不能给错,按顺序

枚举

// enums 枚举

// 数字枚举
// enum Direction {
//   Up,
//   Down,
//   Left,
//   Right,
// }
// console.log(Direction.Up); //0
// console.log(Direction[0]); //'Up'

/* 
// tsc 编译后 -> js文件
var Direction;
(function (Direction) {
    Direction[Direction["Up"] = 0] = "Up";
    Direction[Direction["Down"] = 1] = "Down";
    Direction[Direction["Left"] = 2] = "Left";
    Direction[Direction["Right"] = 3] = "Right";
})(Direction || (Direction = {}));
console.log(Direction.Up); //0
console.log(Direction[0]); //'Up'
*/


// 字符串枚举
// enum Direction {
//   Up = "UP",
//   Down = "DOWN",
//   Left = "LEFT",
//   Right = "RIGHT",
// }
// const value = "UP";
// if (value === Direction.Up) {
//   console.log("go up!");
// }


// 常量枚举 const enum
const enum Direction {
  Up = "UP",
  Down = "DOWN",
  Left = "LEFT",
  Right = "RIGHT",
}
const value = "UP";
if (value === Direction.Up) {
  console.log("go up!");
}

// tsc 编译后 -> js文件
// var value = "UP";
// if (value === "UP" /* Up */) {
//     console.log("go up!");
// }

symbol

// sym2 can only be a constant reference.  不能用let
const sym2: unique symbol = Symbol();

bigint

  • 从 ES2020 开始,JavaScript 中有一个用于非常大的整数的原语BigInt, 当目标低于ES2020时,BigInt字面量不可用。
    在这里插入图片描述
// Creating a bigint via the BigInt function
const oneHundred: bigint = BigInt(100);
 
// Creating a BigInt via the literal syntax
const anotherHundred: bigint = 100n;

interface接口

/* 
interface 接口  :规范 和 契约  规定了样子
对对象的形状进行描述
对类进行抽象
Duck Typing (鸭子类型)

描述  object 的样子
描述 类, 函数 ,数组  都可以用 interface 描述
*/

// 定义一个接口

// 完全匹配形状
interface Person {
  //Person -> IPerson 有的名字前加I 告诉是 Interface
  //这里面属性分隔就是分号分隔,不是逗号
  name: string;
  age: number;
}

// viking -> Person 类型 ,约束 viking 的  object 类型就是Person那样
let viking: Person = {
  //对象里不能少个属性或者多个属性
  name: "songJing",
  age: 20,
};

// 不完全匹配形状 age?: number;
interface PersonLiu {
  name: string;
  age?: number;
}

let vikingLiu: PersonLiu = {
  // age 可以写或者不写
  name: "songJing",
};

// 创建的时候的只读属性  readonly
interface PersonShe {
  readonly id: number; //用于属性上的  放在前面
  name: string;
  age?: number;
}

let vikingShe: PersonShe = {
  id: 1245,
  name: "songJing",
};
// vikingShe.id = 1111;//报错  只读属性,不能重新赋值

函数

// js中函数是一等公民
// 和其他类型平等 传参,存数组,被返回,赋值给变量 等

// function add(x: number, y: number, z: number = 10): number {
//   // y: number, z?: number  必选参数不能位于可选参数后
//   //(x: number, y: number): number   最后的 number   就是返回的类型
//   //  z: number = 10   es6 加默认值&&最后一个参数是可选的
//   if (typeof z === "number") {
//     return x + y + z;
//   } else {
//     return x + y;
//   }
// }

// let result = add(2, 3);

// 函数表达式
const add = function add(x: number, y: number, z: number = 10): number {
  // y: number, z?: number  必选参数不能位于可选参数后
  //(x: number, y: number): number   最后的 number   就是返回的类型
  //  z: number = 10   es6 加默认值  最后一个参数是可选的
  if (typeof z === "number") {
    return x + y + z;
  } else {
    return x + y;
  }
};

// :(x: number, y: number, z?: number) => number   冒号后面是ts中申明类型
// const add2: number = add; //报错
const add2: (x: number, y: number, z?: number) => number = add; //ts类型推测

// let str = "str";
// str = "ddd";
// str = 123;//报错,没有指定类型,ts会自己推测  -> 类型推论

类 :定义一切事物的抽象特点
对象:类的实例
面向对象 oop 封装,继承,多态

类中的修饰符
pulic 共有的
private 属性“name”为私有属性,只能在类“Animal”中访问,外部和子类也不可以访问
protected 子类可以访问属性,外部不可以访问
readonly 只读不能重新赋值

class Animal {
  public name: string;
  //   private name: string;
  //   protected name: string;
  //   readonly name: string;

  //   静态属性
  static categoies: string[] = ["mammal", "bird"];
  //   静态方法:当类中定义和实例的方法没有多大关系时
  static idAnimal(a) {
    return a instanceof Animal;
  }

  constructor(name: string) {
    this.name = name;
  }
  run() {
    return `${this.name} is running`;
  }
}
console.log(Animal.categoies);


const snake = new Animal("lily");
console.log(Animal.idAnimal(snake));

// console.log(snake.run());

// console.log(snake.name);
snake.name = "lucy";
// console.log(snake.name);


class Dog extends Animal {
  bark() {
    return `${this.name} is bark`;
  }
}

const maomao = new Dog("maomao");
// console.log(maomao.run());
// console.log(maomao.bark());


class Cat extends Animal {
  constructor(name) {
    super(name);
    // console.log(this.name);
  }
  run() {
    //super.run  调用父类的方法
    return "Meow," + super.run();
  }
}

const sanhua = new Cat("sanhua");
// console.log(sanhua.run());

类和接口

//两个类都用一个方法,用 interface 定义约束

// class Car {
//   switchRadio() {}
// }

// class Cellphone {
//   switchRadio() {}
// }

// interface  定义约束  implements 抽象类的属性和方法
// 抽象验证类的属性和方法
interface Radio {
  // void 不返回
  switchRadio(triggerL: boolean): void;
}

interface Battery {
  checkBatteryStatus();
}

// 接口也可以继承
interface RadioWithBattery extends Radio {
  checkBatteryStatus();
}

class Car implements RadioWithBattery {
  switchRadio() {}
  checkBatteryStatus() {}
}

// implements Radio, Battery  两个接口,用逗号隔开就行
class Cellphone implements Radio, Battery {
  switchRadio() {}
  checkBatteryStatus() {}
}

泛型

// BUG
// function echo(arg: any): any {
//   return arg;
// }
// const result: string = echo(123);

// 在函数中使用泛型
//泛型出现的动机 ,在函数名称后面加 <>  里面可以写泛型的名称 T  类似占位符或者变量

function echo<T>(arg: T): T {
  return arg;
}
// const str: string = "str";
// const result = echo(str);

// const result: string = echo(true);//报错,不会像之前一样不报错

function swap<T, U>(tuple: [T, U]): [U, T] {
  return [tuple[1], tuple[0]];
}
const result2 = swap(["str", 12]);
// result2[1].  //调用字符串的方法

// 约束泛型
function echoWithArr<T>(arg: T[]): T[] {
  console.log(arg.length);
  return arg;
}
const arrs = echoWithArr([1, 2, 3]);

interface IWithLength {
  length: number;
}

function echoWithLength<T extends IWithLength>(arg: T): T {
  console.log(arg.length);
  return arg;
}
const str = echoWithLength("str");
const obj = echoWithLength({ length: 10, width: 10 });
const arr2 = echoWithLength([1, 2, 3]);

// echoWithLength(13); //报错,类型“13”的参数不能赋给类型“IWithLength”的参数。



// 在类中使用泛型
class Queue<T> {
  private data = [];
  push(item: T) {
    return this.data.push(item);
  }
  pop(): T {
    return this.data.shift();
  }
}
const queue = new Queue<number>();
queue.push(1);
console.log(queue.pop().toFixed());

const queue2 = new Queue<string>();
queue2.push("str");
console.log(queue2.pop().length);

// 在 interface 中使用泛型
interface KeyPair<T, U> {
  key: T;
  value: U;
}
let kp1: KeyPair<number, string> = { key: 123, value: "str" };
let kp2: KeyPair<string, number> = { key: "22", value: 123 };

let arr: number[] = [1, 2, 3];
let arrTwo: Array<number> = [1, 2, 3];

//
interface IPlus<T> {
  (a: T, b: T): T;
}
function plus(a: number, b: number): number {
  return a + b;
}
function connect(a: string, b: string): string {
  return a + b;
}
const a: IPlus<number> = plus;
const b: IPlus<string> = connect;

命名空间

命名空间:
在代码量较大的情况下,为了避免各种变量命名相冲突,可将相似功能的函数、类、接口等放置到命名空间内
同Java的包、.Net的命名空间一样,TypeScript的命名空间可以将代码包裹起来,只对外暴露需要在外部访问的对象。
命名空间和模块的区别:
命名空间:内部模块,主要用于组织代码,避免命名冲突。(不同命名空间,类名称一样也可以)
模块:ts的外部模块的简称,侧重代码的复用,一个模块里可能会有多个命名空间。

/* 
命名空间:在代码量较大的情况下,为了避免各种变量命名相冲突,可将相似功能的函数、类、接口等放置到命名空间内
*/

import { A, B } from "./animal";

var d = new A.Dog("小黑");
d.eat();

var dog = new B.Dog("毛毛");
dog.eat();

  • animal.ts
    export namespace A {
      interface Animal {
        name: string;
        eat(): void;
      }
      export class Dog implements Animal {
        name: string;
        constructor(theName: string) {
          this.name = theName;
        }
        eat() {
          console.log(`${this.name} 在吃狗粮。`);
        }
      }
      export class Cat implements Animal {
        name: string;
        constructor(theName: string) {
          this.name = theName;
        }
        eat() {
          console.log(`${this.name} 在吃猫粮。`);
        }
      }
    }
    export namespace B {
      interface Animal {
        name: string;
        eat(): void;
      }
      export class Dog implements Animal {
        name: string;
        constructor(theName: string) {
          this.name = theName;
        }
        eat() {
          console.log(`${this.name} 在吃狗粮。`);
        }
      }
      export class Cat implements Animal {
        name: string;
        constructor(theName: string) {
          this.name = theName;
        }
        eat() {
          console.log(`${this.name} 在吃猫粮。`);
        }
      }
    }
    

装饰器

通俗的讲装饰器就是一个方法,可以注入到类、方法、属性参数上扩展类、属性、方法、参数的功能。
常见的装饰器有:类装饰器、属性装饰器、方法装饰器、参数装饰器
装饰器的写法:普通装饰器(无法传参)、装饰器工厂(可传参)
装饰器是过去几年js最大的成就之一,以是Es7的标准特性之一

  • 类装饰器:类装饰器在类声明之前被声明(紧靠着类声明)。类装饰器应用于类构造函数,可以用来监视,修改或替换类定义。
  • 装饰器-ES6
  • 装饰器-TypeScript

装饰器的实验支持

  • tsconfig.json
    {
      "compilerOptions": {
        "experimentalDecorators": true //装饰者的实验支持
      }
    }
    

装饰器的写法

  1. 普通装饰器(无法传参)
function logClass(params: any) {
  console.log(params);

  // params 就是当前类
  params.prototype.apiUrl = "动态扩展的属性";

  params.prototype.run = function () {
    console.log("我是一个run方法");
  };
}

@logClass
class HttpClient {
  constructor() {}
  getData() {}
}

var http: any = new HttpClient();
console.log(http.apiUrl);
http.run();
  1. 装饰器工厂(可传参)
function logClass(params: string) {
  return function (target: any) {
    console.log(target);
    console.log(params);

    target.prototype.apiUrl = params;
  };
}

// @logClass("hello")
@logClass("zhainanya")
class HttpClient {
  constructor() {}
  getData() {}
}

var http: any = new HttpClient();
console.log(http.apiUrl);

类装饰器

  • 重载构造函数
  • 类装饰器表达式会在运行时当作函数被调用,类的构造函数作为其唯一的参数。
  • 如果类装饰器返回一个值,它会使用提供的构造函数来替换类的声明
function logClass(target: any) {
  console.log(target);
  return class extends target {
    apiUrl: any = "我是修改后的数据";
    getData() {
      this.apiUrl += "----";
      console.log(this.apiUrl);
    }
  };
}
@logClass
class HttpClient {
  public apiUrl: string | undefined;
  constructor() {
    this.apiUrl = "我是构造函数里面的apiUrl";
  }
  getData() {
    console.log(this.apiUrl);
  }
}
var http = new HttpClient();
http.getData();

属性装饰器

  • 属性装饰器表达式会在运行时当作函数被调用,传入下列两个参数:
    • 对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。
    • 成员的名字。
// 类装饰器
function logClass(params: string) {
  return function (target: any) {
    // console.log(target);
    // console.log(params);
  };
}

// 属性装饰器
function logProperty(params: any) {
  return function (target: any, attr: any) {
    console.log(target);
    console.log(attr);
    target[attr] = params;
  };
}

@logClass("xxx")
class HttpClient {
  @logProperty("xxx.com")
  public url: any | undefined;
  constructor() {}
  getData() {
    console.log(this.url);
  }
}

var http: any = new HttpClient();
http.getData();

方法装饰器

它会被应用到方法的属性描述符上,可以用来监视,修改或者替换方法定义。
方法装饰会在运行时传入下列三个参数:

  • 对于静态成员来说是类的构造函数,对于实例成员是类的原型对象
  • 成员的名字
  • 成员的属性描述符
  • tsc class-decorator.ts --target ES5 -w --experimentalDecorators 运行
  1. 方法装饰器
function get(params: any) {
  return function (target: any, methodName: any, desc: any) {
    console.log(target);
    console.log(methodName);
    console.log(desc);
    target.apiUrl = 123;
    target.run = function () {
      console.log("run");
    };
  };
}

class HttpClient {
  public url: any | undefined;
  constructor() {}

  @get("xxx")
  getData() {
    console.log(this.url);
  }
}

var http: any = new HttpClient();
console.log(http.apiUrl);
http.run();
  1. 方法装饰器
function get(params: any) {
  return function (target: any, methodName: any, desc: any) {
    console.log(target);
    console.log(methodName);
    console.log(desc, desc.value);
    // 修改装饰器的方法,把装饰器方法里面传入的所有参数改为string类型
    var oMthod = desc.value;
    desc.value = function (...args: any[]) {
      args = args.map((value) => {
        return String(value);
      });
      console.log(args, oMthod);
      oMthod.apply(this, args);
    };
  };
}

class HttpClient {
  public url: any | undefined;
  constructor() {}
  @get("maomao")
  getData(...args: any[]) {
    console.log(args, "==");
    console.log("getData=====");
  }
}

var http: any = new HttpClient();
http.getData(123, "xxx");

方法参数装饰器

参数装饰器表达式会在运行时当作函数被调用,可以使用参数装饰器为类的原型增加一些元素数据,传入下列三个参数

  • 对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。
  • 方法的名称
  • 参数在函数参数列表中的索引
     function logParams(params: any) {
       return function (target: any, methodName: any, paramsIndex: any) {
         console.log(params);
         console.log(target);
         console.log(methodName);
         console.log(paramsIndex);
         target.apiUrl = params;
       };
     }
     class HttpClient {
       public url: any | undefined;
       constructor() {}
       getData(@logParams("xxx") uuid: any) {
         console.log(uuid);
       }
     }
     var http: any = new HttpClient();
     http.getData(123);
     console.log(http.apiUrl);
    

装饰器执行顺序

  • 属性装饰器 -> 方法装饰器 -> 方法参数装饰器 -> 类装饰器
  • 同一个方法有多个装饰器,会像剥洋葱一样,先从外到内进入,然后由内向外执行。
    function logClass1(params: string) {
      return function (target: any) {
        console.log("类装饰器1");
      };
    }
    function logClass2(params: string) {
      return function (target: any) {
        console.log("类装饰器2");
      };
    }
    function logAttribute(params?: string) {
      return function (target: any, attrName: any) {
        console.log("属性装饰器");
      };
    }
    function logMethod(params?: string) {
      return function (target: any, attrName: any, desc: any) {
        console.log("方法装饰器");
      };
    }
    function logParams1(params?: string) {
      return function (target: any, attrName: any, desc: any) {
        console.log("方法参数装饰器1");
      };
    }
    function logParams2(params?: string) {
      return function (target: any, attrName: any, desc: any) {
        console.log("方法参数装饰器2");
      };
    }
    @logClass1("zhainanya-1")
    @logClass2("zhainanya-2")
    class HttpClient {
      @logAttribute()
      public apiUrl: string | undefined;
      constructor() {}
      @logMethod()
      getData() {
        return true;
      }
      setData(@logParams1() attr1: any, @logParams2() attr2: any) {}
    }
    var http: any = new HttpClient();
    

其他知识点

  • 类型别名,联合类型,类型断言,声明文件 || 第三方模块

类型别名

  • 类型别名:通常情况下,我们会多次使用同一个类型,并用一个名称来引用它。
    • 使用类型别名来命名任何类型,而不仅仅是对象类型。例如,类型别名可以命名联合类型。
// type aliases 类型别名
type PlusType = (x: number, y: number) => number;
function sum(x: number, y: number) {
  return x + y;
}
const sum2: PlusType = sum;
————
type FetchKeys = (data:any[]) => Promise<string[]>;
interface FetchKeys {
	(data:any[]): Promise<string[]>
}

联合类型

// 联合类型
type NameResolver = () => string;
type NameOrResolver = string | NameResolver;
function getName(n: NameOrResolver): string {
  if (typeof n === "string") {
    return n;
  } else {
    return n();
  }
}
// type assertion 不确定类型,就访问其中的类型或方法

类型断言

  • 与类型注释一样,类型断言会被编译器删除,并且不会影响代码的运行时行为
    • 由于类型断言在编译时被移除,因此不存在与类型断言相关的运行时检查。即使类型断言错误,也不会引发任何异常或生成任何异常。
//类型断言 -》 不是类型转换  断言成联合类型里没有的会报错
function getLength(input: string | number): number {
  //   const str = input as String; //interface
  //   if (str.length) {
  //     return str.length;
  //   } else {
  //     const number = input as Number;
  //     return number.toString().length;
  //   }
  if ((<string>input).length) {
    return (<string>input).length;
  } else {
    return input.toString().length;
  }
}

非空断言运算符

  • 非空断言运算符(后缀!)
    function liveDangerously(x?: number | null) {
      // No error
      console.log(x!.toFixed());
      //'x' is possibly 'null' or 'undefined'.
      //console.log(x.toFixed());
    }
    

类型守卫

  • 当我们在处理复杂的类型时,有时为了方便地对类型进行判断和处理,我们需要用到类型守卫。类型守卫是一种类型保护机制,它可以将变量的类型缩小并且避免由此引起的错误
// typeof
function double(x: number | string): number | string {
  if (typeof x === 'number') {
    return x * 2;
  } else if (typeof x === 'string') {
    return x + x;
  }
}
//instanceof
class MyClass {}
class MySubClass extends MyClass {}

function logClass(x: MyClass | MySubClass) {
  if (x instanceof MySubClass) {
    console.log('x is an instance of MySubClass');
  } else if (x instanceof MyClass) {
    console.log('x is an instance of MyClass');
  }
}
//in
type Person = { name: string; age?: number };

function greet(person: Person) {
  if ('age' in person) {
    console.log(`Hello ${person.name}, you are ${person.age} years old!`);
  } else {
    console.log(`Hello ${person.name}, nice to meet you!`);
  }
}
//is
type Point = { x: number; y: number };

function isPoint(p: Point | any): p is Point {
  return typeof p.x === 'number' && typeof p.y === 'number';
}

function distance(p1: Point | any, p2: Point | any) {
  if (isPoint(p1) && isPoint(p2)) {
    return Math.sqrt(Math.pow(p1.x - p2.x, 2) + Math.pow(p1.y - p2.y, 2));
  } else {
    throw new Error('Invalid arguments: expected points');
  }
}

索引类型

三种索引类型包括:keyof 类型操作符、索引访问类型和类型映射类型。

  • keyof 类型操作符:可以用来获取对象或类的所有属性名,它返回一个类型为字符串字面量联合类型的结果
interface Person {
  name: string;
  age: number;
}

type PersonKeys = keyof Person; // PersonKeys 的结果为 "name" | "age"
  • 索引访问类型:可以用来获取对象或数组的某个属性或元素的类型。它的语法是 Type[KeyType],其中 Type 表示要访问的对象或数组的类型,KeyType 表示属性或元素的键的类型
    interface Person {
     name: string;
     age: number;
    }
    
    • 在 TypeScript 中,使用索引访问类型 [T] 时,如果 T 是一个联合类型,它会对联合类型中的每个成员进行索引访问,并将结果合并成一个新的联合类型。
      type AllFields = 'name' | 'age' | 'email';
      
      // 定义 ExclusiveSort 类型
      type ExclusiveSort<T extends string, K extends T> = {
        [P in T]: P extends K ? 'asc' | 'desc' : false;
      };
      
      // 定义 Sortable 类型
      type Sortable<T extends string> = {
        [K in T]: ExclusiveSort<T, K>;
      }[T];
      
      type SortableResult = Sortable<AllFields>; // type SortableResult = ExclusiveSort<AllFields, "name"> | ExclusiveSort<AllFields, "age"> | ExclusiveSort<AllFields, "email">
      
  • 类型映射类型:允许我们将一个类型映射到另一个类型。它的语法是 { [P in K]: V },其中 P 表示新类型的属性名,K 表示原类型的属性名(可以是 keyof 类型操作符得到的结果),V 表示新类型的属性值
interface Person {
  name: string;
  age: number;
}

type ReadonlyPerson = { readonly [P in keyof Person]: Person[P] };

模版字面量类型

允许我们在类型定义中使用字符串模板。这样做的好处是,可以更灵活地定义类型信息,特别是在定义联合类型时

type EventName = 'click' | 'mouseover' | 'keydown';

type EventFullName<T extends string> = `${T}Event`;

type ClickEventFullName = EventFullName<EventName>; //EventFullName<'click'> ClickEventFullName 的结果为 "clickEvent"

let clickName:ClickEventFullName = 'clickEvent'

映射修饰符

映射修饰符可以用来批量地修改类型映射类型中的属性。映射修饰符包括三种,分别是 readonly、? 和 +/-。
- readonly:指定所有属性为只读属性,语法为 { readonly [P in K]: V }。
- ?:指定所有属性为可选属性,语法为 { [P in K]?: V }。
- +/-:指定属性的修饰符,可以是 +,表示添加修饰符,或者是 -,表示移除修饰符。例如,{ [+P in K]: V } 表示添加 readonly 修饰符。

interface Person {
 readonly name: string;
 readonly age: number;
}

type ReadonlyPerson = { readonly [P in keyof Person]: Person[P] };
type OptionalPerson = { [P in keyof Person]?: Person[P] };
——————————————————————————————————————————————————
interface Person {
 readonly name: string;
 readonly age: number;
}
type OptionalPerson = { -readonly [P in keyof Person]?: Person[P] };

声明文件


// 声明文件 declare   同级文件  jQuery.d.ts

declare var jQuery: (selector: string) => any;

第三方模块

// 第三方模块
// npm install --save @types/jquery
*/

实用工具类型

  • 实用工具类型
    • TypeScript 提供了几种实用程序类型来促进常见的类型转换

      Awaited 是 TypeScript 4.5 版本引入的一个内置工具类型,其主要用途是提取 Promise 类型的解析值类型,或者递归地解开嵌套的 Promise 类型
      Partial 将类型 T 的所有属性变为可选
      Required 将类型 T 的所有属性变为必需
      Readonly 将类型 T 的所有属性设置为只读,这样就无法修改这些属性
      Record 创建一个类型,其属性键为 K,属性值为 T
      Pick 从类型 T 中选取一组属性 K(K 必须是 T 的属性)来构造类型
      Exclude 从类型 T 中排除那些可以赋值给 U 的类型
      Omit 从类型 T 中排除一组属性 K 后的类型
      Extract 从类型 T 中提取那些可以赋值给 U 的类型
      NonNullable 从类型 T 中排除 null 和 undefined
      ReturnType 获取函数类型 T 的返回类型
      InstanceType 获取构造函数类型 T 的实例类型
      Parameters 获取函数类型 T 的参数类型作为一个元组
      ConstructorParameters 提取构造函数类型的参数类型,并将这些参数类型组合成一个元组类型
      NoInfer [TypeScript 5.4版本 ]在泛型参数传递时,该类型参数不会被自动推断,而是由开发者手动指定

    - Awaited<Type> 处理异步函数返回的 Promise, 处理不同的 Promise 嵌套情况
    	- type Awaited<T> = T extends null | undefined ? T : T extends object & {
    	    then(onfulfilled: infer F, ...args: infer _): any;
    	} ? F extends (value: infer V, ...args: infer _) => any ? Awaited<...> : never : T
    - Partial 将类型 T 的所有属性变为可选
      - type Partial<T> = { [P in keyof T]?: T[P] | undefined; }
      - Make all properties in T optional
    - Required 将类型 T 的所有属性变为必需
      - type Required<T> = { [P in keyof T]-?: T[P]; }
      - Make all properties in T required
    - Readonly 将类型 T 的所有属性设置为只读,这样就无法修改这些属性
      - type Readonly<T> = { readonly [P in keyof T]: T[P]; }
      - Make all properties in T readonly
    - Record 创建一个类型,其属性键为 K,属性值为 T - type Record<K extends string | number | symbol, T> = { [P in K]: T; } - Construct a type with a set of properties K of type T
    - Pick 从类型 T 中选取一组属性 KK 必须是 T 的属性)来构造类型
      - type Pick<T, K extends keyof T> = { [P in K]: T[P]; }
      - From T, pick a set of properties whose keys are in the union K
    - Exclude 从类型 T 中排除那些可以赋值给 U 的类型
      值的组合
      - type Exclude<T, U> = T extends U ? never : T
      - Exclude from T those types that are assignable to U
    - Omit 从类型 T 中排除一组属性 K 后的类型
      - type Omit<T, K extends string | number | symbol> = { [P in Exclude<keyof T, K>]: T[P]; }
      - Construct a type with the properties of T except for those in type K.
    - Extract 从类型 T 中提取那些可以赋值给 U 的类型
      - type Extract<T, U> = T extends U ? T : never
      - Extract from T those types that are assignable to U
    - NonNullable 从类型 T 中排除 nullundefined
      - type NonNullable<T> = T & {}
      - Exclude null and undefined from T
    - ReturnType 获取函数类型 T 的返回类型
      - type ReturnType<T extends (...args: any) => any> = T extends (...args: any) => infer R ? R : any
      - Obtain the return type of a function type
    - InstanceType 获取构造函数类型 T 的实例类型
      - type InstanceType<T extends abstract new (...args: any) => any> = T extends abstract new (...args: any) => infer R ? R : any
      - Obtain the return type of a constructor function type
    - Parameters 获取函数类型 T 的参数类型作为一个元组
      - type Parameters<T extends (...args: any) => any> = T extends (...args: infer P) => any ? P : never
      - Obtain the parameters of a function type in a tuple
    - ConstructorParameters 提取构造函数类型的参数类型,并将这些参数类型组合成一个元组类型
    	- type ConstructorParameters<T extends abstract new (...args: any) => any> = T extends abstract new (...args: infer P) => any ? P : never
    	- Obtain the parameters of a constructor function type in a tuple
    - NoInfer 某个参数不被自动推断,而是手动指定以确保类型的精确性时		
    	- type NoInfer<T> = intrinsic
    	- Marker for non-inference type position
    

字符串操作类型 Intrinsic String Manipulation Types

Uppercase<StringType>
Lowercase<StringType>
Capitalize<StringType>
Uncapitalize<StringType>

// Uppercase 示例  将字符串字面量类型转换为大写形式
    // 单个字符串字面量类型 
    type LowercaseString = 'hello';
    type UppercaseResult = Uppercase<LowercaseString>;//"HELLO"

    // 字符串字面量联合类型 
    type MixedCaseStrings = 'hello' | 'world';
    type AllUppercase = Uppercase<MixedCaseStrings>;

// Lowercase 示例 用于将字符串字面量类型转换为小写形式
type UppercaseString = 'HELLO';
type LowercaseResult = Lowercase<UppercaseString>;// 'hello'

// Capitalize 示例 用于将字符串字面量类型的首字母转换为大写。
type lowercaseString = 'hello';
type CapitalizedResult = Capitalize<lowercaseString>;

// Uncapitalize 示例 用于将字符串字面量类型的首字母转换为小写。
type CapitalizedString = 'Hello';
type UncapitalizedResult = Uncapitalize<CapitalizedString>;

自定义类型

列表每个字段单独可排序的,其他字段的类型为 false

// 定义 SortOrder 类型
type SortOrder = 'asc' | 'desc';

// 定义 ExclusiveSort 类型
type ExclusiveSort<T extends string, K extends T> = {
  [P in T]: P extends K ? SortOrder : false;
};

// 定义 Sortable 类型
type Sortable<T extends string> = {
  [K in T]: ExclusiveSort<T, K>;
}[T];

// 示例使用
type AllFields = 'name' | 'age' | 'email';
type SortableResult = Sortable<AllFields>;
// 展开 SortableResult 类型如下
// type SortableResult = 
//   | { name: SortOrder; age: false; email: false; }
//   | { name: false; age: SortOrder; email: false; }
//   | { name: false; age: false; email: SortOrder; }

let validSortA: SortableResult = { name: 'asc', age: false, email: false }; // OK
let validSortB: SortableResult = { name: false, age: 'desc', email: false }; // OK

常见问题

window 添加自定义属性

  • global.d.ts文件
declare global {
  interface Window {
    RM_FONT_FACE: string;
    RM_ANIMATION: string;
  }
}

export {};

interface 和 type 区别

TypeScript 中的 interface 和 type 都可以用来定义对象的形状或者函数签名,但它们在某些方面有所不同。下面是 interface 和 type 的一些主要区别:

  1. 扩展性(Extensibility)
    interface:可以被扩展以构建新的接口。通过使用 extends 关键字,接口可以扩展一个或多个现有的接口。
    type:不可以被扩展,但可以通过交叉类型(使用 & 操作符)来组合现有的类型。
  2. 重复声明(Duplicate Declarations)
    interface:可以在同一作用域内多次声明同一个接口,其成员会自动合并。
    type:不允许同名的重复声明。
  3. 使用场景(Use Cases)
    interface:推荐在定义对象的形状或实现类的场景中使用。它支持声明合并,这在声明文件(例如 .d.ts 文件)或声明类和函数库时十分有用。
    type:适用于定义联合类型、交叉类型、元组类型等复杂类型。类型别名可以声明几乎任何类型,包括基本类型、联合类型和交叉类型。
  4. 实现(Implementation)
    interface:可以通过类来实现(使用 implements 关键字)。
    type:不能被类实现,因为它们可以代表非对象类型(如原始类型、联合和交叉类型)。
  5. 性能(Performance)
    interface:在某些情况下,interface 可能会得到更好的性能,因为它们更容易被TypeScript编译器优化。
    type:由于类型别名可能会直接展开,所以在某些复杂的类型组合中可能会导致编译器和IDE性能下降。

可选的使用

  • 在 TypeScript 里,value: number | undefined; 和 value?: number 虽然在某些情况下表现相似,但实际上存在明显区别
  1. value: number | undefined;
  • value 属性是必须存在的,但其类型可以是 number 或者 undefined
  1. value?: number
  • value 属性是可选的,可以不提供 value 属性。如果提供了 value 属性,其类型必须是 number。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值