Typescript 的基础语法之认识、类数组、元组、接口和类

一、Typescript 的认识
  1. TypescriptJavascript 类型的超集,它可以编译成纯 JavascriptTypescript 可以在任何浏览器、任何计算机和任何操作系统上运行,并且是开源的。
  2. TS 遵循当前以及未来出现的 ECMAScript 规范。TS 不仅能兼容现有的 JavaScript 代码,它也拥有兼容未来版本的 JavaScript 的能力。大多数 TS 的新增特性 都是基于未来的 JavaScript 提案,这意味着许多 TS代码在将来很有可能会变成 ECMA 的标准。
  3. Typescript 的好处,如下所示:
  • TS 的静态类型可以在开发过程中,发现潜在问题
  • 更友好的编辑器自动提示
  • 通过这些静态类型,代码语义更清晰易懂
二、Typescript 的类型
  1. Typescript 的静态类型中,当一个变量是静态类型的时候,不仅仅意味着这个变量的类型不能够修改,还意味着这个变量的属性和方法也基本确定了,编辑器也能够给我们友好的提示。
  2. 在静态类型时,当定义 ponumber 类型的数据时,它就不能够被修改了,同时,也具有 number 的方法。当定义 Point 为自定义类型时,也意味着不能够被修改,具有 Point 的属性和方法,代码如下所示:

const po: number = 2020;

po.toString();

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

const point: Point = {
  x: 3,
  y: 4,
};

  1. Typescript 分为基础类型和对象类型。在基础类型中,包括 number, string, null, undefined, symbol, boolean、void等等。在对象类型中,就是非基础类型,包括 对象类型,数组类型,类类型,函数类型等等,代码如下所示:
const count: number = 123;
const firstName: string = "zhangsan";

class Person {}

const student: { name: string; age: number } = { name: "zhangsan", age: 20 };

const numbers: number[] = [1, 2, 3];

const Jack: Person = new Person();

const getTotal: () => number = () => {
  return 123;
};

  1. type annotation 类型注解, 我们来告诉 TS 变量是什么类型。type inference 类型推断, TS 会自动的去尝试分析变量的类型。如果 TS 能够自动分析变量类型,我们就什么也不需要做了。如果 TS 无法分析变量类型的话,我们就需要使用类型注解。当 firstNumbersecondNumber 的值为 12 的时候,TS 已经做了类型推断,可以知道是 Number 类型,所以 total 也是 Number 类型。any 类型,允许在编译时可选择地包含或移除类型检查。如果不加类型注解,firstlast 就会显示 any, 不知道变量是什么类型,加类型注解,firstlast 就会显示 number,最后 getTotal2 传入值后,tot 也是 Number。对于 obj 对象,TS 也做了类型推断,nameString 类型,ageNumber 类型,代码如下所示:

let countInference = 123;

const firstNumber = 1;
const secondNumber = 2;
const total = firstNumber + secondNumber;

function getTotal2(first: number, last: number) {
  return first + last;
}

const tot = getTotal2(1, 2);

const obj = {
  name: "Jack",
  age: 18,
};

  1. 在函数相关类型中,在给函数的参数做了类型注解以后,对于函数的返回值也要做一层约束,加上类型注解。void 类型像是与 any 类型相反,它表示没有任何类型,只能为它赋予 undefinednull。类型为 void 时,定义 sayHello() 函数为空,没有返回值,不能 return,如果有返回值就会报错。never类型表示的是那些永不存在的值的类型,是任何类型的子类型,也可以赋值给任何类型。类型为 never 时,定义 errorEmitter() 函数永远不可能执行到最后一步。在多个参数的函数中进行解构赋值,需要以对象的形式,并且进行参数的类型注解以及函数返回值的类型注解。对于单个参数的函数中进行解构赋值,同样进行类型注解,代码如下所示:
function add(first: number, last: number): number {
  return first + last;
}

const total2 = add(1, 2);

function sayHello(): void {
  console.log("hello");
}

function errorEmitter(): never {
   throw new Error()
   console.log(123)
}

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

function getNumber({ first }: { first: number }) {
  return first;
}

const total3 = add2({ first: 1, second: 2 });
const count3 = getNumber({ first: 1 });

三、Typescript 的数组、元组和接口
  1. TypeScriptJavaScript 一样可以操作数组元素,它有两种方式可以定义数组。第一种,可以在元素类型后面接上 [],表示由此类型元素组成的一个数组。第二种,可以使用数组泛型,Array<元素类型>。对于数组,基础类型,后面需要跟上 [] 以及它前面的数组的类型注解。对于数组,对象类型,可以使用 type alias 类型别名,后面需要跟上 [] 以及它前面的别名的类型注解,代码如下所示:
const arr: (number | string)[] = [1, 2, 3];
const stringArr: string[] = ["1", "2", "3"];
const undefindArr: undefined[] = [undefined];

type User = { name: string; age: number };

const objectArr: User[] = [{ name: "zhangsan", age: 21 }];

class Teacher {
  name: string;
  age: number;
}

const objectArr2: Teacher[] = [new Teacher(), { name: "zhangsan", age: 21 }];
  1. 元组 tuple,元组类型允许表示一个已知元素数量和类型的数组,各元素的类型不必相同。当数组的长度和类型固定的时候,就可以使用元组进行约束该数组。元组中每一个元素的数量和类型都是与其一一对应的,代码如下所示:

const studentInfo: [string, string, number] = ["Jack", "Tom", 123];

const studentList: [string, string, number][] = [
  ["Tom1", "Jack1", 21],
  ["Tom2", "Jack2", 22],
  ["Tom3", "Jack3", 23],
];

  1. TypeScript的核心原则之一是对值所具有的结构进行类型检查。 它有时被称做“鸭式辨型法”或“结构性子类型化”。 在TypeScript里,接口的作用就是为这些类型命名和为你的代码或第三方代码定义契约。Interface 接口,和 type 相类似,但并不完全一致。类型检查器不会去检查属性的顺序,只要相应的属性存在并且类型也是对的就可以,有几种情况,如下所示:
  • 可选属性,接口里的属性不全都是必需的,与普通的接口定义差不多,只是在可选属性名字定义的后面加一个?符号,好处是可以对可能存在的属性进行预定义和捕获引用了不存在的属性时的错误。
  • 只读属性,一些对象属性只能在对象刚刚创建的时候修改其值。 你可以在属性名前用 readonly来指定只读属性。最简单判断该用readonly还是const的方法是看要把它做为变量使用还是做为一个属性。做为变量使用的话用 const,若做为属性则使用readonly
  • 额外的属性检查,当一个对象字面量里声明了接口中不存在的属性时,会报不存在错误,即使该属性已设置为可选属性,因为该对象字面量会被特殊对待而且会经过额外属性检查。绕开额外属性检查的方法有,使用类型断言,添加一个字符串索引签名,但是前提是你能够确定这个对象可能具有某些做为特殊用途使用的额外属性,将该对象赋值给另一个变量。
  • 函数类型,接口也可以描述函数类型,需要给接口定义一个调用签名。 它就像是一个只有参数列表和返回值类型的函数定义。参数列表里的每个参数都需要名字和类型。
  • 继承接口,能够从一个接口里复制成员到另一个接口里,可以更灵活地将接口分割到可重用的模块里。一个接口可以继承多个接口,创建出多个接口的合成接口。
  • 实现接口,类类型,在接口中描述一个方法,通过 implements 在类里实现它,使用接口的部分内容。接口描述了类的公共部分,而不是公共和私有两部分。 它不会帮你检查类是否具有某些私有成员。
  1. 对于 interface 接口,代码如下所示:

interface Person {
  name2: string;
  age?: number;
  [propName: string]: any;
  say(): string;
}

interface Teacher2 extends Person {
  teach(): string;
}

interface SayHi {
  (word: string): string;
}

const getPersonName = (person: Person): void => {
  console.log(person.name2);
};

const setPersonName = (person: Teacher2, name: string): void => {
  person.name2 = name;
};

const personName = {
  name2: "Jack",
  sex: "man",
  say() {
    return "say hello";
  },
};

const say: SayHi = (word: string) => {
  return word;
};

四、Typescript 的类
  1. 类,使用这些特性,于类的面向对象的方式,并且编译后的JavaScript可以在所有主流浏览器和平台上运行,而不需要等到下个JavaScript版本。定义一个类,有属性、方法和构造函数。基于类的程序设计中一种最基本的模式是允许使用继承来扩展现有的类。子类可以继承父类,也可以重写父类。如下,Student3Person3 的子类,有 Person3name 属性和 getName 方法,也可以新增属性和方法,同时,也可以重写父类的 getName 方法。super 是指父类,通过 super.getName() 调用父类 Person3getName() 方法。对于 super 使用场景,当一个类覆盖掉父类的方法,重写了父类的方法,但是它还想要掉父类的方法,就可以使用 super,代码如下所示:

class Person3 {
  name: "LiSi";
  getName() {
    return this.name;
  }
}

class Student3 extends Person3 {
  getLearnName() {
    return "Typescript";
  }
  getName() {
    return super.getName() + "WangWu";
  }
}

const person3 = new Person3();
console.log(person3.getName());

const student3 = new Student3();
console.log(student3.getLearnName());

  1. 对于类中的访问类型,分为三种,private, protected, publicpublic 允许在类的内外被调用,private 允许在类内被使用, protected 允许在类内及继承的子类中使用。当一个类中,属性和方法什么都不写,说明是 public 类型,默认是隐藏的。对于 constructor 构造器,当实例化一个类的时候,构造器会在创建的时候自动执行,而传递类的属性的时候,实际上会被这个类的构造器所接收到。当子类继承于父类的时候,如果想要私有构造器 constructor,必须使用 super 调用父类的构造器。如果父类构造器为空,没有使用 constructor,那么也需要使用 super,传入空值,代码如下所示:
class Person4 {
  protected name: string;
  public sayHi() {
    console.log("hi");
  }
  private sayHe() {
    console.log("he");
  }
}

class Student4 extends Person4 {
  public satBye() {
    this.sayHi();
  }
}

const person = new Person4();
// person.name = "ZhaoLiu";
// console.log(person.name);
person.sayHi();

class Person5 {
  constructor(public name: string) {}
}

class Teacher5 extends Person5 {
  constructor(public age: number) {
    super("Lucy");
  }
}

const person5 = new Person5("XiongDa");
console.log(person5.name);

const teacher5 = new Teacher5(29);
console.log(teacher5.name);
console.log(teacher5.age);

  1. TypeScript 支持通过 getters/setters 来截取对对象成员的访问, 它能帮助你有效的控制对对象成员的访问。存取器要求你将编译器设置为输出 ECMAScript 5 或更高, 不支持降级到ECMAScript 3。 其次,只带有 get 不带有 set 的存取器自动被推断为 readonly。创建类的静态成员,这些属性存在于类本身上面而不是类的实例上,可以使用 static。在类中的构造器中,添加 private,给属性添加私有保护,只能够在类中访问,在类外是访问不到的。getset 是提供了一种类外访问私有属性的方式,可以在外部去获取和设置私有属性,但是不改变在类中的值。如下,_name 是私有属性,通过 getsetname 是 类 Person6 实例化对象 person6 的一个属性,get 是可读,set 是可写,代码如下所示:
class Person6 {
  constructor(private _name: string) {}
  get name() {
    return this._name + "Tom";
  }
  set name(name: string) {
    const realName = name.split(" ")[0];
    this._name = realName;
  }
}

const person6 = new Person6("Jake");
console.log(person6.name);

person6.name = "zhang san";
console.log(person6.name);

// 单例模式

class Demo {
  private static instance: Demo;
  private constructor(public name: string) {}

  static getInstance() {
    if (!this.instance) {
      this.instance = new Demo("dell lee");
    }
    return this.instance;
  }
}

const demo1 = Demo.getInstance();
const demo2 = Demo.getInstance();
console.log(demo1.name);
console.log(demo2.name);

  1. 抽象类做为其它派生类的基类使用,它们一般不会直接被实例化。 不同于接口,抽象类可以包含成员的实现细节, abstract 关键字是用于定义抽象类和在抽象类内部定义抽象方法。抽象类中的抽象方法不包含具体实现并且必须在派生类中实现。 抽象方法的语法与接口方法相似, 两者都是定义方法签名但不包含方法体。然而,抽象方法必须包含 abstract 关键字并且可以包含访问修饰符。当如果想要类中的属性只能够只读,不能够写,第一种方式是设置类中的属性为 public 公有,设置权限为 readonly 只读。第二种方式是 设置类中的属性为 private 私有,通过 get 让属性具有只读的权限。抽象类,通过 abstract 定义一个抽象类 Geom,通过 abstract 定义一个抽象方法 getArea(),不包含具体实现但是必须在派生类中实现,Circle 是继承于 抽象类 Geom,是它的基类,必须有抽象类中抽象方法的具体实现 getArea()。对于具有相同属性的接口进行一次封装,Person 都具有 name 属性,对于 Person 接口也有不同的实现,可以继承 Person 再继续封装,添加新的对应的属性,StudentTeacher 添加对应的 studentAgeteacherAge 属性,这样就实现了接口的封装,得以更好的利用,接口的适用,代码如下所示:
class Person7 {
  public readonly name: string;
  constructor(name: string) {
    this.name = name;
  }
}

const person7 = new Person7("Tom");
console.log(person7.name);

abstract class Geom {
  width: number;
  getType() {
    return "Gemo";
  }
  abstract getArea(): number;
}

class Circle extends Geom {
  getArea() {
    return 123;
  }
}

interface Person {
  name: string;
}

interface Student extends Person {
  studentAge: number;
}

interface Teacher extends Person {
  teacherAge: number;
}

const student = {
  name: "ZhangSan",
  studentAge: 18,
};

const teacher = {
  name: "LiSi",
  teacherAge: 28,
};

const getUserInfo = (user: Person) => {
  console.log(user.name);
};

getUserInfo(student);
getUserInfo(teacher);

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值