TypeScript进阶语法

1. 初始命令

初始化项目: tsc --init 会创建一个tsconfig.json配置文件。
编译ts文件: tsc demo.ts 将.ts文件转换成demo.js文件。

执行tsc demo.ts 并不会读取tsconfig.json文件配置,只有执行tsc才会根据配置文件信息编译ts文件

tsconfig.json配置项:

配置功能
removeComments移除注释
noImplicitAnyfalse :不必指定所有数据类型 (在strict为false的情况下)
strictNullChecksfalse: 不对null值进行校验
incremental增量式编译
allowJs允许对js文件进行编译
checkJs对js文件进行语法检测
noUnusedLocals对未使用的变量进行检测

2. 类型保护

as : 通过自身对代码的理解,确保代码不会出错

// 联合类型的类型保护
interface Bird {
  fly: boolean;
  sing: () => {};
}

interface Dog {
  fly: boolean;
  bark: () => {};
}

// 类型断言1: 变量名 as 类型
function trainAnimal(animal: Bird | Dog) {
  if (animal.fly) {
    (animal as Bird).sing();
  } else {
    (animal as Dog).bark();
  }
}

// 类型断言2:<类型>变量名
let length: number = (<string>value).length;

// 类型保护:in
function trainAnimalSecond(animal: Bird | Dog) {
  if ('sing' in animal) {
    animal.sing();
  } else {
    animal.bark();
  }
}

// 类型保护:typeof
function add(first: string | number, second: string | number) {
  if (typeof first === 'string' || typeof second === 'string') {
    return `${first}${second}`;
  }
  return first + second;
}

// 类型保护:instanceof
// 这里必须用class类来定义,只有类能用instanceof调用
class NumberObj {
  count: number;
}
function addSecond(first: object | NumberObj, second: object | NumberObj) {
  if (first instanceof NumberObj && second instanceof NumberObj) {
    return first.count + second.count;
  }
  return 0;
}

3. 枚举类型

enum Status {
  OFFLINE = 1, // 设置值为1(枚举默认从0开始)
  ONLINE,
  DELETED
}

// key value 可形成映射关系
console.log(Status.OFFLINE, Status[1]); // 1 OFFLINE

4. 泛型

  • 函数泛型
// 泛型 generic 泛指的类型,根据调用的时候传递的参数类型

function join<T, P>(first: T, second: P) {
  return `${first}${second}`;
}

join<number, string>(1, '1');  // 显式指定T和P的类型
join(1, '1'); // ts自动根据传递的参数推断

// T[]
function map<T>(params: Array<T>) {
  return params;
}

map<string>(['123']); 
  • 类中的泛型
interface Item {
  name: string;
}

// 泛型有一个name属性
class DataManager<T extends Item> {
  constructor(private data: T[]) {}
  // 返回的是string类型
  getItem(index: number): string {
    return this.data[index].name;
  }
}

// 传递一个对象的数组
const data = new DataManager([{ name: 'dell' }]);
data.getItem(0);

5. namespace 命名空间

避免使用过多全局变量。(类模块化的方式)

  • 使用命名空间
namespace Home {
  class Header {
    constructor() {
      const elem = document.createElement('div');
      elem.innerText = 'This is Header';
      document.body.appendChild(elem);
    }
  }

  class Content {
    constructor() {
      const elem = document.createElement('div');
      elem.innerText = 'This is Content';
      document.body.appendChild(elem);
    }
  }

  // 命名空间中暴露出的变量,用于外部使用
  export class Page {
    constructor() {
      new Header();
      new Content();
    }
  }
}

使用: new Home.Page();

  • 命名空间引用
/* namespace相互依赖的声明:Home命名空间依赖components.ts的内容 */
///<reference path="components.ts" />

namespace Home {
  export class Page {
    constructor() {
      new Components.Header();
      new Components.Content();
    }
  }
}

tsconfig.jsoutFile 设置到一个文件中就可以使用其他文件下的命名空间:

{
  "compilerOptions": {  
    "target": "es5" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */,
    "module": "amd" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */,

    // 将所有输出的文件打包到一个文件中,此时module不支持commonjs模块化的这种方式, 只支持amd/umd
    "outFile": "./dist/page.js" /* Concatenate and emit output to single file. */,
    "outDir": "./dist" /* Redirect output structure to the directory. */,
    "rootDir": "./src" /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */,
    
  }
}

6. parcel : 直接编写ts代码

html 中引入的ts代码进行编译成浏览器能够识别的代码。
文件路径:src/index.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>Document</title>
    <script src="./page.ts"></script>
  </head>
  <body></body>
</html>

运行:parcel ./src/index.html

7. .d.ts 类型定义文件:为了帮助理解ts文件

  • 变量类型定义
    page.ts文件:
$(function () {
  $('body').html('<div>123</div>');
});

针对上述ts文件,对应的jquery.d.ts内容:

// 定义全局变量: $ 接收的参数是一个函数
// declare var $: (param: () => void) => void;

// 定义全局函数
interface JqueryInstance {
  html: (html: string) => JqueryInstance;
}

// 函数重载:允许对同一个函数名字$,写全局函数的声明
declare function $(readyFunc: () => void): void;
declare function $(selector: string): JqueryInstance;

// 或 使用interface实现函数重载
interface JQuery {
	(readyFunc: () => void): void;
	(selector: string): JqueryInstance;
}
declare var $:JQuery;
  • 声明全局变量
    TypeScript declare的用法

    declare var wx: any;
    //  declare 的意思是声明,var 的全称是 variable 表示变量。wx 是全局变量的名称,而 any 表示该变量的类型。
    
  • 如何对对象以及类进行类型定义

ts文件: new $.fn.init();

对应的类型jquery.d.ts文件:

  // $命名空间下的fn命名空间中有一个init的构造函数
 declare namespace $ {
    namespace fn {
      class init {}
    }
  }
  • ES6 模块化的类型
    ts文件:
import $ from 'jquery';

$(function() {
  $('body').html('<div>123</div>');
  new $.fn.init();
});

jquery.d.ts文件:declare module

// Es6 模块化的描述文件
declare module 'jquery' {
  interface JqueryInstance {
    html: (html: string) => JqueryInstance;
  }
  // 混合类型
  function $(readyFunc: () => void): void;
  function $(selector: string): JqueryInstance;
  namespace $ {
    namespace fn {
      class init {}
    }
  }
  export = $;
}

8. 泛型中typeof的使用

interface Person {
  name: string;
  age: number;
  gender: string;
}
// keyof Person : 对Person中的接口值进行遍历
class Teacher {
  constructor(private info: Person) {}
  getInfo<T extends keyof Person>(key: T): Person[T] {
    return this.info[key];
  }
}
// 使用解释:(type 可以是字符串)
// (T extends keyof Person):  type T = 'gender'
// (key: T) :       key: 'gender'
// (Person[T]) :    Person['gender']

const teacher = new Teacher({
  name: 'dell',
  age: 18,
  gender: 'male'
});

const test = teacher.getInfo('name');
console.log(test);

这样就限制了getInfo 的入参只能是Person中定义的三个变量,并且能够确定返回值类型。

9. 对项目中的一些依赖的使用

  • 根据业务场景获取时需要增加准确的类型描述
import { Router, Request, Response } from 'express';

// 继承原有类型描述
interface RequestWithBody extends Request {
  body: {
    [key: string]: string | undefined;
  };
}

router.post('/getData', (req: RequestWithBody, res: Response) => {
  const { password } = req.body; // 业务场景值
  if (password === '123') {
    res.send('getData Success!');
  } else {
    res.send(`${req.teacherName} password Error!`);
  }
});
  • 设置时使用类型融合
import express, { Request, Response, NextFunction } from 'express';
app.use((req: Request, res: Response, next: NextFunction) => {
   // 当设置的值是依赖中不存在的变量时,可以定义custom.d.ts文件,使用类型融合
  req.teacherName = 'dell';
  next();
});

custom.d.ts 类型定义,ts会进行类型融合:

// 类型融合
declare namespace Express {
  interface Request {
    teacherName: string;
  }
}

10. 装饰器

装饰器本身是一个函数,类装饰器接受的参数是构造函数,装饰器通过 @ 符号来使用。装饰器在类创建好之后就立即执行。
tsconfig.json配置支持装饰器 。

{
  "compilerOptions": {
  	 "experimentalDecorators": true /* Enables experimental support for ES7 decorators. */,
    "emitDecoratorMetadata": true /* Enables experimental support for emitting type metadata for decorators. */,
  }
}
  • 类的装饰器
// 通过返回一个函数的方式来接收变量
function testDecorator(flag: boolean) {
  if (flag) {
    return function(constructor: any) {
      constructor.prototype.getName = () => {
        console.log('dell');
      };
    };
  } else {
    return function(constructor: any) {};
  }
}

@testDecorator(true)
class Test {}

const test = new Test();
(test as any).getName();

  • 方法装饰器
// 普通方法,target 对应的是类的 prototype
// 静态方法,target 对应的是类的构造函数

function getNameDecorator(target: any, key: string, descriptor: PropertyDescriptor) {
  // console.log(target, key);
  // descriptor.writable = true;  // 装饰器的方法是否允许修改 
  descriptor.value = function() { // 修改方法的返回值
    return 'decorator';
  };
}

class Test {
  name: string;
  constructor(name: string) {
    this.name = name;
  }
  @getNameDecorator
  getName() {
    return this.name;
  }
}

const test = new Test('dell');
console.log(test.getName());

方法的错误捕获:

const userInfo: any = undefined;

function catchError(msg: string) {
  return function(target: any, key: string, descriptor: PropertyDescriptor) {
    const fn = descriptor.value;
    descriptor.value = function() {
      try {
        fn();
      } catch (e) {
        console.log(msg);
      }
    };
  };
}

class Test {
  @catchError('userInfo.name 不存在')
  getName() {
    return userInfo.name;
  }
  @catchError('userInfo.age 不存在')
  getAge() {
    return userInfo.age;
  }
  @catchError('userInfo.gender 不存在')
  getGender() {
    return userInfo.gender;
  }
}

const test = new Test();
test.getName();
test.getAge();

  • 属性装饰器
// 修改的并不是实例上的 name, 而是原型上的 name
function nameDecorator(target: any, key: string): any {
  target[key] = 'lee';
}

// name 放在实例上
class Test {
  @nameDecorator
  name = 'Dell';
}

const test = new Test();
console.log((test as any).__proto__.name);
  • 参数装饰器
// 原型,方法名,参数所在的位置
function paramDecorator(target: any, method: string, paramIndex: number) {
  console.log(target, method, paramIndex);
}

class Test {
  getInfo(name: string, @paramDecorator age: number) {
    console.log(name, age);
  }
}

const test = new Test();
test.getInfo('Dell', 30);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值