TypeScript背景和语法介绍

本文详细介绍TypeScript的起源、特点及其在大型项目中的应用优势。深入解析TypeScript的安装、编译流程,数据类型系统,函数与类的高级用法,以及接口和泛型的实践,帮助开发者掌握TypeScript,提升代码质量和可维护性。
摘要由CSDN通过智能技术生成

TypeScript介绍

官网

  1. TypeScript 是由微软开发的一款开源的编程语言。
  2. TypeScript 是 Javascript 的超集,遵循最新的 ES6、Es5 规范。TypeScript 扩展了 JavaScript 的语法。
  3. TypeScript 更像后端 java、C#这样的面向对象语言,可以让 js 开发大型企业项目。
  4. 谷歌也在大力支持 Typescript 的推广,谷歌的 angular2.x+就是基于 Typescript 语法。
  5. 最新的 Vue 、React 也可以集成 TypeScript。
  6. Nodejs 框架 Nestjs、midway 中用的就是 TypeScript 语法。
  7. 带有类型,是说js在定义变量的时候,类型是动态的,只有在运行的时候才能知道它的具体类型,比如 number 或者 string,并且类型也是可以动态变化的,而 TypeScript 则是要求变量有确定的类型,并且在编写代码的时候就已经确定,如果把字符串赋给类型为 number ,数字类型的变量,就会出错。

为什么用 TypeScript

认识了 TypeScript 之后,可能你又有问题,为什么要学 TypeScript 呢?先看一条数据,在 stackoverflow 发起的2020年程序员调查中,TypeScript 在程序员最爱的编程语言中排在了第二位,仅次于 Rust:程序员最爱的编程语言(最新2020/5/27)

之所以大家喜欢 TypeScript,是因为:

  • TypeScript 有类型检查机制,我们可以在写代码的时候就能够发现错误,比如给函数误传了类型不同的参数,那么通过 VS Code 对 TypeScript 的强力支持,我们能立刻看到错误。
  • 另外 VS Code 能根据 TypeScript 的类型信息提供更好的代码提示和补全功能。
    此外,对于大型项目、多人协作编写代码时,类型起到了文档的作用,可以清楚的知道我这个变量是什么类型,或者我定义的函数需要什么样的参数,我的对象里又有哪些属性。这样让代码更易于维护,这也是为什么大公司、大型项目更偏爱 TypeScript
  • 最后 TypeScript 入门的门槛低,只要你会 JavaScript,那么你就已经能编写 TypeScript 代码了。另外因为 JS 的快速发展,好多以前在 typescript 才能用的功能,你可能在JS 里已经用到了,所以说要学习的东西就更少了。

除了这些好处之外,它也有其他静态类型语言比如 Java/c++ 的通病,就是代码量会增加,并且有时候类型过于复杂反而使得代码显的更难阅读,不过跟它带来的优势相比,也显得不那么突出了。

TypeScript安装、编译

安装

npm install -g typescript 
或者cnpm install -g typescript 
或者yarn global add typescript

运行

tsc helloworld.ts

TypeScript 开发工具 Vscode 自动编译.ts 文件

  1. 创建 tsconfig.json 文件 tsc --init 生成配置文件
  2. 老版本 vscode 点击: 任务->运行任务-> tsc:监视-tsconfig.json 然后就可以自动生成代码了
  3. 新版本 vscode 点击: 终端->运行任务->typescript->tsc:监视-tsconfig.json 然后就 可以自动生成代码了

TypeScript中的数据类型

typescript中为了使编写的代码更规范,更有利于维护,增加了类型校验,在typescript中主要给我们提供了以下数据类型:

给变量定义数据类型有两种方式,一种是隐式的,一种是显式的

隐式

隐式类型是由 TypeScript 根据变量的值来推断类型,这样的话,代码的写法跟 JS 就一样了,但不同的是它后边不能用其他类型的值来给他重新赋值

let a = 10;
a = "hello";
// error TS2322: Type '"hello"' is not assignable to type 'number'.
显式

显式类型的定义,就跟之前运行的 TS 代码示例一样,我们用 : + 类型 来显式的规定,这个变量是什么类型的

  • 布尔类型(boolean)

    var flag:boolean = true;
    flag = false;
    console.log(flag);
    
  • 数字类型(number)

  • 字符串类型(string)

  • 数组类型(array)

    1. 第一种写法:
    var arr:number[] = [11, 22, 33];
    console.log(arr);
    
    1. 第二种写法:
    var arr1:Array<any> = ['11', 22, '33'];
    console.log(arr1);
    
    1. 第三种写法:
    var arr2:any[] = ['131214',22,true];
    console.log(arr2);
    
  • 元组类型(tuple)— 属于数组的一种

    let arr3:[number, string] = [123, 'this is ts'];
    console.log(arr3);
    
  • 枚举类型(enum)

    在其它程序设计语言中,一般用一个数值来代表某一状态,这种处理方法不直观,易读性差。

    如果能在程序中用自然语言中有相应含义的单词来代表某一状态,则程序就很容易阅读和理解。也就是说,事先考虑到某一变量可能取的值,尽量用自然语言中含义清楚的单词来表示它的每一个值,这种方法称为枚举方法,用这种方法定义的类型称枚举类型。

    enum Status {success=1, error=2};
    let s:Status = Status.success;
    console.log(s);
    
    enum Color {blue, red, orange};
    let c:Color = Color.red;
    console.log(c); //1 如果标识符没有赋值 它的值就是下标
    
  • 任意类型(any)

    var num:any = 123;
    num = 'str';
    num = true;
    console.log(num);
    
    • 任意类型的用处(不加类型会报错)
    var oBox:any=document.getElementById('box');
    oBox.style.color='red';
    
  • 对象类型(object)

    var arr2:object = [11, 'asd', 1321];
    console.log(arr2);
    
  • null 和 undefined
    其他(never类型)数据类型的子类型

    var num:number;
    console.log(num)  //输出:undefined   报错
    var num1:undefined;
    console.log(num1)  //输出:undefined  //正确
    var num2:number | undefined;
    num2=123;
    console.log(num2);
    
  • void类型

    typescript中的void表示没有任何类型,一般用于定义方法的时候方法没有返回值。

    function run():void{
      console.log('run');
    }
    run();
    
    function run1():number{
      return 123;
    }
    run1();
    
  • never类型

    是其他类型 (包括 null 和 undefined)的子类型,代表从不会出现的值。这意味着声明never的变量只能被never类型所赋值。

    var a: never;
    a = 123; //错误写法
    a = (function () {
      throw new Error("错误")
    })();
    
    //返回never的函数必须存在无法达到的终点
    function infiniteLoop(): never {
      while (true) {}
    }
    
  • 组合类型
    如果一个变量可以有多个类型,但是又不想使用any破坏类型检查,那么可以使用组合类型,组合类型使用一条竖线,也就是或操作符来定义。

    let a: number | string = 10;
    a = "hello";
    

    另外,组合类型也可以直接使用字面值来定义,这样就规定了一个变量的取值范围

    let c: "on" | "off" = "on";
    c = "off";
    c = "other"; //错误
    
  • 类型别名
    这样代码看起来不太方便,并且这个组合类型只能给 a 使用,如果有一个变量 b 也可以同时是 number 或 string 的类型的话,还要重复定义这个类型。要解决这个问题,我们可以使用 type 关键字来给这个组合类型起个别名,让代码更易读,也方便其他变量使用,这里定义一个 type 名字叫 NumStr,自定义的类型名字推荐首字母大写

TypeScript中的函数

  1. 函数的定义
// 有返回值
function run(): string {
  return 'run';
}
// 无返回值
function run(): void{
  console.log('run');
}
// 箭头函数
const materials = [
  'Hydrogen',
  'Helium',
  'Lithium',
  'Beryllium'
];

console.log(materials.map((material): any => material.length));// 加不加any都可以
  • 定义方法中的传参
function getInfo(name: string, age: number): string {
  return `${name} --- ${age}`;
}
alert(getInfo("zhangsan", 20));
  1. 可选参数

es5里面方法的实参和行参可以不一样,但是ts中必须一样,如果不一样就需要配置可选参数

function getInfo(name: string, age?: number): string {
  if (age) {
    return `${name} --- ${age}`;
  } else {
    return `${name} ---年龄保密`;
  }
}
alert(getInfo('zhangsan'))
alert(getInfo('zhangsan', 123))

注意:可选参数必须配置到参数的最后面

  1. 默认参数

es5里面没法设置默认参数,es6和ts中都可以设置默认参数

function getInfo(name: string, age: number = 20): string {
  if (age) {
    return `${name} --- ${age}`;
  } else {
    return `${name} ---年龄保密`;
  }
}
// alert( getInfo('张三'));
alert(getInfo('张三', 30));
  1. 剩余参数

必要参数,默认参数和可选参数有个共同点:它们表示某一个参数。 有时,你想同时操作多个参数,或者你并不知道会有多少参数传递进来。 在JavaScript里,你可以使用 arguments来访问所有传入的参数。在TypeScript里,你可以把所有参数收集到一个变量里。

剩余参数会被当做个数不限的可选参数。 可以一个都没有,同样也可以有任意个。

三点运算符 接受新参传过来的值

function sum(...result: number[]): number {
  var sum = 0;
  for (var i = 0; i < result.length; i++) {
    sum += result[i];
  }
  return sum;
}
alert(sum(1, 2, 3, 4, 5, 6));
function sum(a: number, b: number, ...result: number[]): number {
  var sum = a + b;
  for (var i = 0; i < result.length; i++) {
    sum += result[i];
  }
  return sum;
}
alert(sum(1, 2, 3, 4, 5, 6));
  1. 函数重载

java中方法的重载:重载指的是两个或者两个以上同名函数,但它们的参数不一样,这时会出现函数重载的情况

TS中的重载:通过为同一个函数提供多个函数类型定义来试下多种功能的目的

  • es5中出现同名方法,下面的会替换上面的方法
function css(config){

}

function css(config,value){

}
  • ts中的重载
function getInfo(name: string): string;
function getInfo(age: number): string;
function getInfo(str: any): any {
  if (typeof str === 'string') {
    return '我叫:' + str;
  } else {
    return '我的年龄是' + str;
  }
}
alert(getInfo('张三'));   //正确
alert(getInfo(20));   //正确
// alert(getInfo(true));    //错误写法
function getInfo(name: string): string;
function getInfo(name: string, age: number): string;
function getInfo(name: any, age?: any): any {
  if (age) {
    return '我叫:' + name + '我的年龄是' + age;
  } else {
    return '我叫:' + name;
  }
}
alert(getInfo('zhangsan'));  //正确
alert(getInfo(123));  //错误
alert(getInfo('zhangsan', 20));

TypeScript中的类

  1. ts中类的定义
class Person {
  name: string;   //属性  前面省略了public关键词
  constructor(n: string) {  //构造函数   实例化类的时候触发的方法
    this.name = n;
  }
  run(): void {
    alert(this.name);
  }
}
var p = new Person('张三');
p.run()
class Person {
  name: string;
  constructor(name: string) {  //构造函数   实例化类的时候触发的方法
    this.name = name;
  }
  getName(): string {
    return this.name;
  }
  setName(name: string): void {
    this.name = name;
  }
}
var p = new Person('张三');
alert(p.getName());
p.setName('李四');
alert(p.getName());
  1. ts中实现继承 extends、 super

ts中继承的探讨 父类的方法和子类的方法一致

class Person {
  name: string;
  constructor(name: string) {
    this.name = name;
  }
  run(): string {
    return `${this.name}在运动`
  }
}
var p = new Person('王五');
alert(p.run())

class Web extends Person {
  constructor(name: string) {
    super(name);  /*初始化父类的构造函数*/
  }
  work() {
    alert(`${this.name}在工作`)
  }
}
var w = new Web('李四');
alert(w.run());
alert(w.work());
  1. 类里面的修饰符

typescript里面定义属性的时候给我们提供了三种修饰符: public/protected/private

  • public : 公有 在当前类里面、 子类 、类外面都可以访问

子类中:

class Person {
public name: string;  /*公有属性*/
  constructor(name: string) {
    this.name = name;
  }
  run(): string {
    return `${this.name}在运动`
    }
  }
}
class Web extends Person {
  constructor(name: string) {
    super(name);  /*初始化父类的构造函数*/
  }
  run(): string {
    return `${this.name}在运动-子类`
  }
  work() {
    alert(`${this.name}在工作`)
  }
}
var w = new Web('李四');
w.work();

类外部访问公有属性:

class Person {
public name: string;  /*公有属性*/
constructor(name: string) {
  this.name = name;
}
run(): string {
  return `${this.name}在运动`
  }
}
var p = new Person('哈哈哈');
alert(p.name);
  • protected:保护类型 在类里面、子类里面可以访问 ,在类外部没法访问

子类中:

class Person {
  protected name: string;  /*保护属性*/
  constructor(name: string) {
    this.name = name;
  }
  run(): string {
    return `${this.name}在运动`
  }
}

class Web extends Person {
  constructor(name: string) {
    super(name);  /*初始化父类的构造函数*/
  }
  work() {
    alert(`${this.name}在工作`)
  }
}
var w = new Web('李四11');
w.work();
alert(w.run());

类外外部没法访问保护类型的属性:

class Person {
  protected name: string;  /*保护类型*/
  constructor(name: string) {
    this.name = name;
  }
  run(): string {
    return `${this.name}在运动`
  }
}
var p = new Person('哈哈哈');
alert(p.name);
  • private :私有 在类里面可以访问,子类、类外部都没法访问

子类中:

class Person {
  private name: string;  /*私有*/
  constructor(name: string) {
    this.name = name;
  }
  run(): string {
    return `${this.name}在运动`
  }
}
class Web extends Person {
  constructor(name: string) {
    super(name)
  }
  work() {
    console.log(`${this.name}在工作`)
  }
}

类里:

class Person {
private name: string;  /*私有*/
constructor(name: string) {
  this.name = name;
}
run(): string {
  return `${this.name}在运动`
  }
}
var p = new Person('哈哈哈');
alert(p.run());
  1. 静态属性 静态方法
class Per {
  public name: string;
  public age: number = 20;

  //静态属性
  static sex = "男";
  constructor(name: string) {
    this.name = name;
  }
  run() {  /*实例方法*/
    alert(`${this.name}在运动`)
  }
  work() {
    alert(`${this.name}在工作`)
  }
  static print() {  /*静态方法  里面没法直接调用类里面的属性*/
    alert('print方法' + Per.sex);
  }
}

Per.print();
alert(Per.sex);
  1. 多态 抽象类
  • 多态

父类定义一个方法不去实现,让继承它的子类去实现 每一个子类有不同的表现

class Animal {
  name: string;
  constructor(name: string) {
    this.name = name;
  }
  eat() {   //具体吃什么  不知道   ,  具体吃什么?继承它的子类去实现 ,每一个子类的表现不一样
    console.log('吃的方法')
  }
}

class Dog extends Animal {
  constructor(name: string) {
    super(name)
  }
  eat() {
    console.log(this.name + '啃骨头')
  }
}
class Cat extends Animal {
  constructor(name: string) {
    super(name)
  }
  eat() {
    console.log(this.name + '吃鱼')
  }
}
let ANAME: string = "Tom";
var D = new Dog(ANAME);
D.eat();
var C = new Cat(ANAME);
C.eat();
  • 抽象类

    typescript中的抽象类:它是提供其他类继承的基类,不能直接被实例化

    用abstract关键字定义抽象类和抽象方法,抽象类中的抽象方法不包含具体实现并且必须在派生类中实现

    abstract抽象方法只能放在抽象类里面

    abstract class Animal {
    public name: string;
    constructor(name: string) {
      this.name = name;
    }
    abstract eat(): any;  //抽象方法不包含具体实现并且必须在派生类中实现。
    run() {
      console.log('其他方法可以不实现')
      }
    }
    // var a=new Animal() /*错误的写法*/
    
    class Dog extends Animal {
      //抽象类的子类必须实现抽象类里面的抽象方法
      constructor(name: any) {
        super(name)
      }
      eat() {
        console.log(this.name + '吃粮食')
      }
    }
    
    class Cat extends Animal {
      //抽象类的子类必须实现抽象类里面的抽象方法
      constructor(name: any) {
        super(name)
      }
      eat() {
        console.log(this.name + '吃老鼠')
      }
    }
    
    var d = new Dog('小花花');
    d.eat();
    var c = new Cat('小花猫');
    c.eat();
    

TypeScript中的接口

接口的作用:在面向对象的编程中,接口是一种规范的定义,它定义了行为和动作的规范,在程序设计里面,接口起到一种限制和规范的作用。

接口定义了某一批类所需要遵守的规范,接口不关心这些类的内部状态数据,也不关心这些类里方法的实现细节,它只规定这批类里必须提供某些方法,提供这些方法的类就可以满足实际需要。

typescrip中的接口类似于java,同时还增加了更灵活的接口类型,包括属性、函数、可索引和类等

  • ts中自定义方法传入参数,对json进行约束
function printLabel(labelInfo:{label:string}):void {
  console.log('printLabel', labelInfo);
}

// printLabel('hahah'); //错误写法
// printLabel({name:'张三'});  //错误的写法
printLabel({label:'张三'});  //正确的写法
  • 接口:行为和动作的规范,对批量方法进行约束
//就是传入对象的约束    属性接口
interface FullName {
  firstName: string;   //注意;结束
  secondName: string;
}

function printName(name: FullName) {
  // 必须传入对象  firstName  secondName
  console.log(name.firstName + '--' + name.secondName);
}
// printName('1213');  //错误

var obj = {   /*传入的参数必须包含 firstName  secondName*/
  age: 20,
  firstName: '张',
  secondName: '三'
};
printName(obj)
  • 参数的顺序可以不一样
interface FullName {
  firstName: string;
  secondName: string;
}

function getName(name: FullName) {
  console.log(name)
}
//参数的顺序可以不一样
getName({
  secondName: 'secondName',
  firstName: 'firstName'
})
  • 也可以设置可选属性
interface FullName {
  firstName: string;
  secondName?: string;
}

function getName(name: FullName) {
  console.log(name)
}
getName({
  firstName: 'firstName'
})
case:ajax
interface Config {
  type: string;
  url: string;
  data?: string;
  dataType: string;
}

//原生js封装的ajax 
function ajax(config: Config) {
  var xhr = new XMLHttpRequest();
  xhr.open(config.type, config.url, true);
  xhr.send(config.data);
  xhr.onreadystatechange = function () {
    if (xhr.readyState == 4 && xhr.status == 200) {
      console.log('success!!!');
      if (config.dataType == 'json') {
        console.log(JSON.parse(xhr.responseText));
      } else {
        console.log(xhr.responseText)
      }
    }
  }
}

ajax({
  type: 'get',
  data: 'name=zhangsan',
  url: 'http://a.itying.com/api/productlist', //api
  dataType: 'json'
})

TypeScript中的泛型

软件工程中,我们不仅要创建一致的定义良好的API,同时也要考虑可重用性。 组件不仅能够支持当前的数据类型,同时也能支持未来的数据类型,这在创建大型系统时为你提供了十分灵活的功能。

在像C#和Java这样的语言中,可以使用泛型来创建可重用的组件,一个组件可以支持多种类型的数据。 这样用户就可以以自己的数据类型来使用组件。

function add<T>(a: T, b: T): void {
    console.log(a)
    console.log(b)
}
add(1, 2)
add('a', 'b')

接口 + 泛型 -> 函数类型

let addFn: AddFn<boolean> = function (a, b) {
    console.log(a)
    console.log(b)
    return [{ id: 1, name: '浙大正呈' }]
}
interface arrIF {
    readonly id: number,
    name: string
}
interface AddFn<T> {
    (a: T, b: number): arrIF[]
}

console.log(addFn(false, 2))
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值