乐趣区

关于前端:走近Ts用了爽用后一直爽一

前言

vue3 曾经公布了,ts 的脚步曾经拦截不住了,还只会 es6? 别想了,人家都曾经在口头了,以下是 ts 的根本系列教程,ts 的根本语法,高级语法等,以及在 vue 我的项目中如何利用 ts,跟着我连忙撸起来吧。

根本数据类型

数字
const a: number = 3;
字符串
const b: string = "1";
数组
const c: number[] = [1, 2, 3];
const d: Array<number> = [1, 3];
const arr: any[] = [1, "33", true];
元组

能够为数组中的每个参数定义绝对应的类型

const e: [number, string] = [1, "ww"];
枚举
enum error {
  blue = 3,
  "orange",
}
const f: error = error.orange;
console.log(f); // 输入 4 

tips

  1. 如果 未赋值 的上 一个值是数字 那么这个 未赋值的值 的是上一个值的值 +1
  2. 如果 未赋值 上一个值未赋值 那么输入的就是它的 下标
  3. 如果 未赋值的上一个值的值是非数字, 那么必须赋值
布尔类型
const g: boolean = true;
对象

const i: object = {};
undefined

罕用于组合类型

let j: number | undefined;
null
let k: null;
void

指定办法类型,示意没有返回值, 办法体中不能return

function aa(): void {console.log(1);
}

// 如果办法有返回值,能够加上返回值的类型
function bb(): number {return 1;}
never

其余类型 (包含 null 和 undefined)的子类型,代表从不会呈现的值

let l: never;

// 匿名函数并抛出异样
l = (() => {throw new Error("111");
})();
任意类型

让参数能够是任何一种类型


let h: any = 1;
h = true;
h = "st";

函数

函数申明
function cc(): void {}
办法传参
function getUserInfo(name: string, age?: number, school: string = "清华大学") {return `name:${name}--age:${age}--school:${school}`;
}

tips: ? 代表这个参数可传可不传, 不传就是 undefined, 也可定义个默认的值

残余参数

传递多个时,如果用了残余参数,就能够把未定义的形参转换为数组。

function sum (a: number, b: number, ...arr: number[]): number {
  let sum: number = a + b;
  arr.forEach((element) => {sum += element;});
  console.log(arr); [3,4,5]  
  return sum;
}
console.log(sum(1, 2, 3, 4, 5)); //15
函数重载
function reload(name: string): string;
function reload(age: number): string;
function reload(param: any): any {return typeof param === "string" ? ` 我是:${param}` : ` 我的年龄:${param}`;
}
console.log(reload(18)); // 年龄

tips: 被重载 的办法,是 没有办法体 ,能够依据参数的类型走其中一个办法并判断参数,但如果 传入的参数类型不是任何被重载办法的参数类型 就不容许通过。

 第 1 个重载(共 2 个),“(name: string): string”,呈现以下谬误。类型“never[]”的参数不能赋给类型“string”的参数。第 2 个重载(共 2 个),“(age: number): string”,呈现以下谬误。类型“never[]”的参数不能赋给类型“number”的参数


class Person {
  // 公有变量
  private name: string;
  
  // 构造函数
  constructor(name: string) {this.name = name;}
  
  // 获取名字
  getName(): string {return this.name;}
  
  // 设置名字
  setName(name: string): void  {this.name = name;}
}

let p = new Person("张三");
p.setName("李四");
console.log(p);

继承

class Son extends Person {
 // 动态属性
  public static age: number = 18;
  // 学校
  public school: string;
  // 构造方法
  constructor(name: string, school: string) {
    // 拜访派生类的构造函数中的 "this" 前,必须调用 "super", 初始化父类构造函数 -- 并把参数传给父类
    super(name); 
    // 把传进来的 school 赋值给全局变量
    this.school = school;
  }
  // 静态方法
  static run(name: string): string {return `${name}在跑步, 他的年龄才 ${this.age}`;
  }
}

let son = new Son("王五", "清华大学");
son.setName("赵六"); // 公有类也不能在子类的内部拜访, 但可通过公开的办法中进行赋值和拜访
console.log(son);
console.log(Son.run("方七"));
console.log(Son.age);

tips:

  1. public 在以后类外面,子类,类里面都能够拜访
  2. protected 在以后类和子类外部能够拜访,类内部无法访问
  3. private 在以后类外部可拜访,子类,类内部都无法访问。
  4. 属性不加修饰符, 默认就是私有的 (public)

多态

通过形象办法 / 办法重载 – 实现多态 – 多态的作用是用来定义规范

// 形象父类
abstract class Animal {
  private name: string;
  constructor(name: string) {this.name = name;}
  // 形象成员 -- 办法
  abstract eat(): any;
  // 形象成员 -- 属性
  protected abstract ages: Number;
  sleep(): void {console.log("睡觉");
  }
}

class cat extends Animal {
  ages: Number = 2;
  constructor(name: string) {super(name);
  }
  // 非抽象类“cat”不会主动实现继承自“Animal”类的形象成员“eat”,  必须手动定义父类中的形象办法 -- 多态
  eat(): string {return "猫吃鱼";}

  // 多态
  sleep(): string {return "猫在睡觉";}
}

console.log(new cat("33").sleep());

tips:

  1. 抽象类无奈 实例化
  2. 非抽象类继承形象父类时 不会主动实现 来自父类的形象成员, 必须 手动定义 父类中的形象成员,否则报错。
  3. 形象成员包含 属性 办法

接口

  在面向对象的编程中,接口是一种标准的定义,它定义了行为和动作的标准,

  在程序设计外面,接口起到一种限度和标准的作用。

  接口定义了某一批类所须要恪守的标准,接口不关怀这些类的外部状态数据,也不关怀这些类里办法的实现细节,它只规定这批类里必须提供某些办法,提供这些办法的类就能够满足理论须要。ts 中的接口相似于 java,同时还减少了更灵便的接口类型,包含属性、函数、可索引和类等。

属性接口
interface InterfaceName {
  first: string;
  second?: string; // 加个问号,接口属性就能够变成可传可不传了,不传默认是 undefined。}
// 打印变量
function logParam(name: InterfaceName): void {console.log(name.first, name.second, 11);
}
// 定义参数
const obj = {first: "1", second: "fff", three: 1};
//logParam({first: "1", second: "1", three: 1}); // 报错, 只能传接口定义的值
logParam(obj);

tips: 用个变量来存储传入的变量, 这样能够传入定义的接口以外的值,否则如果间接传入对象中无接口定义的值会报错,所以倡议接口定义了哪些值就传哪些值。

函数类型接口

对办法传入的参数类型, 以及返回值类型进行束缚, 可批量进行束缚。

interface keyMap {(key: string, value: string): string;
}
let logKeyMap: keyMap = function (key1: string, value: string): string {return key1 + value;};
console.log(logKeyMap("key1", "value"));

tips: 接口只对传入的参数的类型和参数的个数进行束缚,不对参数名称进行束缚。

可索引接口
  • 束缚数组
interface Arr {[index: number]: string;
}
let ss: Arr = ["2121"];
  • 束缚对象
interface Obj {[index: string]: string;
}

let interfaceArr: Obj = {aa: "1"};

tips:

  1. 数组 进行束缚,index后必须跟着 number 类型。
  2. 对象 进行束缚,index后必须跟着 string 类型
  3. 索引签名参数类型必须为 “string” 或 “number”
类类型接口
  • 进行束缚, 相似 抽象类 的实现。
interface Animals {
  name: string;
  eat(): void;}

class Dogs implements Animals {
  name: string;
  constructor(name: string) {this.name = name;}
  eat() {}
}
  • 接口继承 – 接口能够继承接口
interface Dog {eat(): void;
}

interface Persons extends Dog {work(): void;
}

class Cat {code() {console.log("猫在敲代码");
  }
}

// 可继承类后再实现接口
class SuperMan extends Cat implements Persons {eat(): void {console.log(1);
  }
  work(): void {console.log(2);
  }
}
let superMan = new SuperMan();
superMan.code();

tips: 类接口会对类的 属性 办法 进行束缚,相似非抽象类继承抽象类时必须实现某些办法和属性,但对属性和办法的类型的束缚更加严格,除了办法 void 类型 可被 从新定义 外,其余属性或办法的类型定义须要和接口保持一致。

泛型

  软件工程中,咱们不仅要创立统一的定义良好的 api,同时也要思考可重用性。
组件不仅可能反对以后的数据类型,同时也能反对将来的数据类型,这在创立大型零碎时为你提供了非常灵便的性能

   泛型就是解决 接口 办法 复用性 ,以及对 不特定数据类型 的反对。

  要求: 传入的参数和返回的参数统一

函数的泛型
function getDate<T>(value: T): T {return value;}
console.log(getDate<number>(123));

tips: 这里的 T 可改成其余任意值但定义的值,和传入的参数以及返回的参数是一样的, 个别默认写法是 T,也是业内标准的抉择。

类的泛型
class MinClass<T> {public list: T[] = [];
  // 增加
  add(value: T): void {this.list.push(value);
  }
  
  // 求最小值
  min(): T {
    // 假如这个值最小
    let minNum = this.list[0];
    for (let i = 0; i < this.list.length; i++) {
    // 比拟并获取最小值
    minNum = minNum < this.list[i] ? minNum : this.list[i];
    }
    return minNum;
  }
}
// 实例化类 并且指定了类的 T 的类型是 number
let minClass = new MinClass<number>(); 
minClass.add(23);
minClass.add(5);
minClass.add(2);
console.log(minClass.min());
 // 实例化类 并且指定了类的 T 的类型是 string,则其办法的传参和返回都是 string 类型
let minClass2 = new MinClass<string>();
minClass2.add("23");
minClass2.add("5");
minClass2.add("2");
console.log(minClass2.min());
接口的泛型
  • 第一种写法
interface ConfigFn {
  // 标准参数类型, 返回值类型
  <T>(value: T): T;
}

let getData: ConfigFn = function <T>(value: T): T {return value;};

console.log(getData<string>("z11"));
  • 第二种写法

interface ConfigFn<T> {
  // 参数类型,返回值类型
  (value: T): T;
}

// 接口办法
function getData<T>(value: T): T {return value;}

// 应用接口
let myGetDate: ConfigFn<string> = getData;
console.log(myGetDate("3"));

tips: 接口的泛型只针对函数类型的接口

类当做参数传入泛型类
// 用户类 -- 和数据库表字段进行映射
class User {
  username: string | undefined;
  password: string | undefined;
  // 构造函数 - 初始化参数
  constructor(param: {
    username: string | undefined;
    password?: string | undefined;
  }) {
    this.username = param.username;
    this.password = param.password;
  }
}


// 数据库类
class Db<T> {add(user: T): boolean {console.log(user);
    return true;
  }
  updated(user: T, id: number): boolean {console.log(user, id);
    return true;
  }
}

let u = new User({username: "张三",});

//u.username = "李四";
u.password = "111111";
let db = new Db<User>();
db.add(u);
db.updated(u, 1);

tips: 类的参数名和类型都做了束缚。

模块

  外部模块称为命名空间,内部模块简称为模块,模块在其本身的作用域里执行,而不是在全局作用域里;

  这意味着定义在一个模块里的变量、函数、类等等在模块内部是不可见的,除非你明确的应用 export 模式之一导出它们。

  相同,如果想应用其它模块导出的变量,函数,类,接口等的时候,你必须要导人它们,能够应用 import 模式之一。

  咱们能够一些公共的性能独自抽离成一个文件作为一个模块。
模块外面的变量、函数、类等默认是公有的,如果咱们要在内部拜访模块外面的数据 (变量、函数、类)
咱们须要通过 export 裸露模块外面的数据 (变量、函数、类 …)。
裸露后咱们通过 import 引入模块就能够应用模块外面裸露的数据(变量、函数、类 …)

//modules/db.ts
function getDate(): any[] {console.log("获取数据");
  return [
    {userName: "张三",},

    {userName: "李四",},
  ];
}

// 一个模块外面能够用屡次
// export {getDate};
// 一个模块外面只能用一次
export default getDate;
 import {getDate as getDbDate} from "./modules/db";
 import getDbDate from "./modules/db";
 getDbDate();

tips: 这个调试时浏览器中不能间接应用, 可在 nodeweakpack的环境中调试。

命名空间

  在代码量较大的状况下,为了防止各种变量命名相冲突,可将类似性能的函数、类、接口等搁置到命名空间内
TypeScript 的命名空间能够将代码包裹起来,只对外裸露须要在内部拜访的对象。

   命名空间和模块的区别

  • 命名空间:外部模块,次要用于组织代码,防止命名抵触。
  • 模块:ts 内部模块的简称,偏重代码的复用,一个模块里可能会有多个命名空间。
 // modules/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 namespace B {
  interface Animal {
    name: String;
    eat(): void;}

  export class Dog implements Animal {
    name: String;
    constructor(theName: string) {this.name = theName;}
    eat() {}
  }
}
 import {A, B} from "./modules/Animal";
 let ee = new A.Dog("小贝");
 ee.eat();

装璜器

  • 类装璜器:类装璜器在类申明之前被申明(紧靠着类申明),类装璜器利用于类构造函数,能够用于监督,批改或者替换类定义。
function logClass(params: any) {console.log(params);
  //params 就是指代以后类 --HttpClient
  params.prototype.apiUrl = "动静扩大属性";
  params.prototype.run = function () {console.log("动静扩大办法");
  };
  params.prototype.getDate = function () {console.log("动静扩大办法 2");
  };
}

@logClass
class HttpClient {constructor() {}
  getDate() {console.log(1);
  }
}

let http: any = new HttpClient();
console.log(http.apiUrl);
http.run();
http.getDate();

tips: 装璜器会笼罩被装璜的类中的办法。

  • 装璜器工厂

可传参的装璜器


function logClassB(param: string) {return function (target: any) {console.log(target, "装璜器以下的类");
    console.log(param, "装璜器传进来的属性");
  };
}

@logClassB("小慧")
class HttpClients {constructor() {}
  getDate() {}
}

let https: any = new HttpClients();
console.log(https);
  • 构造函数装璜器
function logClassC(target: any) {console.log(target, 1111);
  // 用在这里继承指标类并重载办法和属性
  return class extends target {
    a: any = "我是批改后的属性";
    getDate() {console.log(this.a + "-- 装璜器中的办法输入的");
    }
  };
}

@logClassC
class HttpClient2 {
  public a: string | undefined;
  constructor() {this.a = "我是构造函数外面的 a";}
  getDate() {console.log(this.a);
  }
}
const https2 = new HttpClient2();
https2.getDate();
未完待续~

下期预报: 在 vue 中应用 ts。

退出移动版