【专项学习】—— Typescript语法进阶

一、TypeScript中的配置文件

①生成tsconfig.json文件

npm install typescript -g

tsc init

②tsconfig.json文件的作用是typescript对应的编译配置文件 

③运行tsc demo.ts会编译成demo.js文件

④只有单独输入tsc命令,后面不跟任何参数时,才会执行tsconfig里面的配置

   ts-node demo.ts也会执行tsconfig里面的配置项

⑤compilerOptions前(或后)添加include指定想要编译的文件 

"include": ["./demo.ts"]

同"files":["./demo.ts"]

也可以写exclude指定编译所有内容所要排除不编译的文件

"exclude": ["./demo.ts"]

⑥compilerOptions是编译属性配置项  

   "removeComments": true,   //移除注释

   "noImpliciAny": true,    //不明确的指定any类型不行

   "stictNummChecks": true,    //强制检测Null类型,将null赋值给基础类型会报错

   "rootDir": "./src",    //指定输入文件的地址

   "outDir": "./build",  //指定编译后生成文件的地址

   "incremental": true,  //增量编译,以前编译过的内容不会再编译,只编译新增的文件

   "allowJs": true,    //是否对js文件也进行编译,把ES6语法编译为ES5语法文件

   "checkJs": true,   //允许对js文件像ts文件一样进行语法检测

 

二、联合类型和类型保护

interface Bird {
  fly: boolean;
  sing: () => {};
}

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

//联合类型 - 属性只会取共有的属性
function trainAnimal(animal: Bird | Dog ) {
   //1.类型断言 — 类型保护,规避typescript的潜在错误提示
   if(animal.fly) {
      (animal as Bird).sing();
   }else{
      (animal as Dog).bark()
   }
   
}

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

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

//只要class 才能被instanceof操作符使用,interface不能
class NumberObj {
  count: number
}

function addSecond(first: object | NumberObj, second: object | NumberObj){
  //4.instanceof 语法 - 类型保护
  if(first instanceof NumberObj && second instanceof NumberObj){
    return first.count + second.count
  }
  return 0;
}

 

三、Enum枚举类型 

①初始化项目,生成package.json

npm init -y  

②安装ts-node依赖

npm install ts-node -D  

③package.json中添加命令

"dev": "ts-node ./demo.ts"  

④安装typescript

npm install typescript --save

⑤Enum枚举类型应用  

enum Status {
  OFFLINE,
  ONLINE,
  DELETED
}

// const Status = {
//   OFFLINE: 0,
//   ONLINE: 1,
//   DELETED: 2
// }

function getResult(status) {
  if(status === Status.OFFLINE) {
    return 'offline';
  }else if(status === Status.ONLINE){
    return 'online';
  }else if(status === Status.DELETED){
    return 'deleted';
  }
  return 'error'
}

const result = getResult(Status.OFFLINE);
console.log(result);  //offline

  

  

 

四、函数泛型

//泛型 generic 泛指的类型

//T可以是任何类型的泛型
function join<T, P>(first: T, second: P) {
  return `${first}${second}`;
}

join<number, string>(1, '1');
// T[]
function map<T>(params: Array<T>){
  return params;
}

map<string>(['123']);

  

五、类中的泛型类型以及泛型类型

①类中的泛型类型

interface Item {
  name: string;
}

class DataManager<T extends Item> {
  constructor(private data: T[]) {}
  getItem(index: number): string {
    return this.data[index].name;
  }
}

// const data = new DataManager<number>([1]);
// data.getItem(0);

const data = new DataManager([
  {
    name: 'dell'
  }
])
class DataManager<T extends number | string> {
  constructor(private data: T[]) {}
  getItem(index: number): T {
    return this.data[index]
  }
}

// const data = new DataManager<string>([]);
const data = new DataManager<number>([]);

②泛型声明的type类型  

// const func: <T>() => string = <T>() => {
//   return '123'
// }

//如何使用泛型作为一个具体的类型注解
function hello<T>(param: T) {
  return param
}

const func: <T>(param: T) => T = hello;

 

六、命名空间namespace  

①tsconfig.json

    "outDir": "./dist",             
    "rootDir": "./src", 

②index.html  

<!DOCTYPE html>
<html lang="en">
  <head>
     <meta charset="UTF-8" />
     <title>Document</title>
     <script src='./dist/page.js'></script>
  </head>
  <body>
     <script>
        new Home.Page();
     </script>
  </body>
</html>

③src->page.ts: Home命名空间中,export暴露出要用的page,index.html中就可以使用new Home.page();其它方法不使用则不暴露  

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);
    }
  }
  
  class Footer {
    constructor() {
      const elem = document.createElement('div');
      elem.innerText = 'This is Footer';
      document.body.appendChild(elem);
    }
  }
  
  export class Page {
    constructor() {
      new Header();
      new Content();
      new Footer();
    }
  }
}
  • 拆分页面和组件多个命名空间

①tsconfig.json 

"outFile": "./dist/page.js" , //把所有要输出的文件联合起来输出一个单一的文件  

此时会有报错,因为设置了outFile后只支持amd或system的规范,不支持commenjs的规范了

"module": "amd",

②index.html不变

③src->page.ts依赖conponents.ts

//依赖声明
///<reference path="./components.ts" />

namespace Home {
  export class Page {
    user: Components.User = {
      name: 'dell'
    }
    constructor() {
      new Components.Header();
      new Components.Content();
      new Components.Footer();
    }
  }
}

④src->components.ts暴露给page.ts所需要的方法和子命名空间  

namespace Components {
  
  //子的命名空间
  export namespace SubComponents {
    export class Test {}
  }

  export interface User {
    name: string;
  }

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

⑤问题:page.ts中依赖的Components命名空间必须在打开其它文件后才可以追溯到  

 

七、import对应的模块化  

①src->components.ts:export暴露三个变量  

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

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

②src->page.ts:import导入components的变量,export default最终要用到的Page  

import { Header, Content, Footer } from './components'

export default class Page {
    constructor() {
      new Header();
      new Content();
      new Footer();
    }
}

③问题:'define' is not defined,原因是现在打包生成的文件是amd规范,浏览器不识别define()语法  

 

 ④index.html中引入require.js,帮助识别define()语法;

<!DOCTYPE html>
<html lang="en">
  <head>
     <meta charset="UTF-8" />
     <title>Document</title>
     <script src='https://cdnjs.cloudflare.com/ajax/libs/require.js/2.3.6/require.js'></script>
     <script src='./dist/page.js'></script>
  </head>
  <body>
     <script>
       require(['page'], function(page) {
         new page.default()
       })
     </script>
  </body>
</html>

 

八、使用parcel打包TS代码  

①安装parcel@next打包工具

npm install parcel@next -D  

②package.json中修改test命令

"test": "parcel ./index.html"  

③运行npm run test,开启一个服务器地址http://localhost:1234

 

九、描述文件中的全局类型

①index.html中引用jquery的CDN文件

<!DOCTYPE html>
<html lang="en">
  <head>
     <meta charset="UTF-8" />
     <title>Document</title>
     <script src='https://cdn.bootcss.com/jquery/3.4.1/jquery.js'></script>
     <script src='./page.ts'></script>
  </head>
  <body>
  </body>
</html>

②jquey语法在typescript中没有注解,需要写一个类型定义文件,打通ts文件和js文件之间的鸿沟  

③src->page.ts:使用jqurey写js语句

// $(function() {
//   alert(123);
// })

$(function() {
  $('body').html('<div>123</div>')
})

④src->jquery.d.ts:类型注解文件  

//declare 声明 - 定义全局变量
// declare var $ : (param: () => void) => void;

//declare 声明 - 定义全局函数
declare function $(param: () => void): void;

//函数重载:参数不同,允许对一个函数名字写多个函数声明
declare function $(param: string): {
  html: (html: string) => {}
}

 问题:jquery函数返回的应该也是一个jqury对象,不能指定为{}

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

declare function $(readyFunc: () => void): void;
declare function $(selector: string): JqueryInstance;

使用 接口语法 写函数重载  

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

//使用interface的语法,实现函数重载
interface JQuery {
  (readyFunc: () => void): void;
  (selector: string): JqueryInstance;
}

declare var $: Jquery;
  • 如何对对象进行类型定义,以及对类进行类型定义,以及命名空间的嵌套

①page.ts:在使用$('body')时,相当于执行了new $.fn.init();  

$(function() {
  $('body').html('<div>123</div>');
  new $.fn.init(); //构造函数 才能用new语法
})

②jquery.d.ts  

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

//函数重载
declare function $(readyFunc: () => void): void;
declare function $(selector: string): JqueryInstance;

//如何对对象进行类型定义,以及对类进行类型定义,以及命名空间的嵌套
declare namespace $ {  //$ 是命名空间
  namespace fn {       //fn 是命名空间
    class init {}      //init 是构造函数
  }
}

 

十、模块代码的类型描述文件  

 ①安装jquery 

npm install jquery  --save

 ②page.ts:import引用jquery,会提示报错,找不到jquery module  

import $ from 'jquery';

$(function() {
  $('body').html('<div>123</div>');
  new $.fn.init(); //构造函数 才能用new语法
})

③jquery.d.ts: 通过module定义一个jquery的模块,定义关于$混合类型的内容,最终export导出要使用的$ 

//ES6 模块化
declare module 'jquery' {
  //定义全局函数
  interface JqueryInstance {
    html: (html: string) => JqueryInstance;
  }

  //$ 混合类型 - 不必再写declare, 直接写function即可
  function $(readyFunc: () => void): void;
  function $(selector: string): JqueryInstance;

  namespace $ {  //$ 是命名空间
    namespace fn {       //fn 是命名空间
      class init {}      //init 是构造函数
    }
  }

  export = $;
}

 

十一、泛型中keyof语法的使用  

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

class Teacher {
  constructor(private info: Person) {}
  getInfo(key: string){
    //类型保护
    if(key === 'name' || key === 'age' || key === 'gender'){
      return this.info[key];
    }
  }
}

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

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

问题:test的可能类型会是string | number | underfind,是因为传任何值给getInfo方法时,key值不定

            typescript不会深度分析,传入的是不是name类型,如果是则值必须是string类型;传入的是age类型,值必须是number类型

解决:通过泛型结合keyof语法  

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

//第一次循环
//T extends 'name'
//type T = 'name'
//key: 'name'
//Person['name'];  //string

//第二次循环
//T extends 'age'
//type T = 'age'
//key: 'age'
//Person['age'];   //number     

class Teacher {
  constructor(private info: Person) {}
  getInfo<T extends keyof Person>(key: T): Person[T]{
    return this.info[key];
  }
}

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

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

注:课程源自慕课网 

posted @ 2021-03-12 13:41  柳洁琼Elena  阅读(181)  评论(0编辑  收藏  举报