TypeScript(一)

420 阅读7分钟

概述

js开发中存在的问题

  1. 使用了不存在的变量、函数或成员;
  2. 把一个不确定的类型当做一个确定的类型处理;
  3. 在使用null或undefined的成员。(TypeError: Cannot- read property 'name' of undefined,在统计的前端开发排名前10的错误中,这个错误排第一);

js和ts代码对比

  1. 看下面简单的一段程序,将姓名的首字母大写后反转返回;
    下面的程序中有几处错误,如果在js中,我们只有在代码运行时才能发现错误;
function getUserName() {
    if (Math.random() < 0.5) {
        return 'zhang yin'
    };

    return 404;
}

let myname = getUsername();
mynema = myname.split(' ').map(el => el[0].touppercase + el.subStr(1)).join(' ');
  1. 现在什么都不用做,只把js后缀换成ts,错误立刻就显示出来了,ts不会等到执行时才报错。
ts代码
ts代码
  • 点击快速修复
ts代码
ts代码
  • 还有错误,提示myname可能是string或者number,number上没有split方法。加判断
ts代码
ts代码
  • 还有错误,一直点快速修复
ts代码
ts代码
ts代码
ts代码
  • 完美,错误全部修复
ts代码
ts代码

js的原罪

  1. js语言本身的特点,决定了该语言无法适应大型的复杂的项目;
  2. 弱类型:某个变量,可以随时更换类型;
  3. 解释性:错误发生的时间,是在运行时;

前端开发中,大部分时间都是在排错;

TypeScript (ts = js + 类型系统)

  1. TypeScript是js的超集,是一个可选的、静态的类型系统;
  2. 超集:包含的关系,整数是正整数的超集;
  3. 类型系统:对代码中所用的标识符(变量、函数、参数、返回值)进行类型检查;
  4. 可选的,学习曲线相对平滑;
  5. ts不参与任何运行时的类型检查;
  6. ts在编写时报错,js在执行时报错;

ts的常识

  1. 2012年微软发布;
  2. Anders Hejlsberg 负责开发ts项目;
  3. 开源的,拥抱ES标准;

额外的惊喜

  1. 有了类型检查,无形中增强了面向对象的开发;
  2. js中也有类和对象,js支持面向对象开发,没有类型检查,很多面向对象的场景实现起来会有诸多问题;
  3. 使用ts后,可以编写出完善的面向对象代码;

参考文献

在node中搭建ts开发环境

安装TypeScript

npm i typescript -g

将ts编译成js

tsc index.ts

默认情况下,TS会做出下面几种假设:

  1. 假设当前的执行环境是浏览器环境(Dom);
  2. 如果代码中没有使用模块化语句(import、export),便认为该代码是时全局执行;
  3. 默认编译的目标代码是ES3;

解决方法

  1. 使用tsc命令行的时候,加上选项参数;
  2. 使用ts配置文件,更改编译选项;

TypeScript的配置文件

生成tsconfig.json

tsc --init

使用了配置文件后,使用tsc进行编译时,不能跟上文件名,如果跟上文件名,会忽略配置文件

@types/node

npm i -D @types/node

@types/node是一个ts官方的类型库,其中包含了很多对js代码的类型描述;

例:
JQuery:用js写的,没有类型检查;
安装@types/jquery,为jquery库添加类型定义;

使用第三方库简化流程

ts-node:将ts代码在内存中完成编译,同时完成运行

npm i ts-node -g ts-node /src/index.ts

nodemon:用于检测文件的变化

npm i nodemon -g
nodemon --exec ts-node sec/index.ts

写成script脚本:

"dev": "nodemon --watch src -e ts --exec ts-node src/index.ts" // 只监控src下的ts文件

基本类型检查

类型约束和编译结果对比

编译结果中没有类型约束信息;

如何进行类型约束

  1. ts 是一个可选的静态类型系统;
  2. ts 在很多场景中可以完成类型推导;
  3. any 表示任意类型,ts 不进行类型约束;
  4. 如何区分数字字符串和数字,关键看怎么读?如果按照数字的方式朗读,则为数字,否则为字符串

基本类型检查

  1. number
  2. string
  3. boolean
  4. array
  5. object
  6. null 和 undefined 是所有其它类型的子类型,它们可以复制给其它类型;
    "strictNullChecks": true, 可以获得更严格的空类型检查,null 和 undefined 只能赋值给自身

其它常用类型

  1. 联合类型:多种类型任选其一 ;
  • 配合类型保护进行判断;
  • 类型保护:当对某个变量进行类型判断之后,在判断的语句块中便可以确定它的确切类型,typeof 可以触发类型保护;
let name: string | undefined = undefined;
if (typeof name === "string") {
    name.toUpperCase();
};
  1. void 类型:通常用于约束函数的返回值,表示该函数没有任何返回;
  2. never 类型:通常用于约束函数的返回值,表示该函数永远不会结束;
function demo () {
    throw new Error("错误");
    console.log("永远不会执行");
};
function demo1 () {
    while(true){
        cconsole.log('never')
    }
};
  1. 字面量类型: 使用一个值进行约束
let arr: []; // arr永远只能取值为一个空数组
let gender'男' | '女';
let user: {
    namestring
    agenumber// 一个对象,必须要name和age
  1. 元祖类型(Tuple):一个固定类型的数组,并且数组中每一项的类型确定
let arr: [string, number];  
arr = ['zy'21];
  1. any类型:可以绕过类型检查,any类型可以赋值给任意类型
let name: any = 'zhangyin';
let age: number = any

类型别名

类型别名:对已经类型定义名称;

type Gender = 'man' | 'womam';
type User = {
  name: string
  age: number
  gender: Gender
}
let person: User;
person = {
  name: 'zhangyin',
  gender: 'man',
  age: 21,
}
function getUsers (g: Gender): User[] {
  return[];
}

函数的相关约束

  1. 函数重载:在函数实现之前,对函数调用的多种方式进行声明
function combine (anumberbnumber): number;
function combine (astringbstring): string;
function combine (anumber | stringbnumber | string): number | string {
  if (typeof a === 'number' && typeof b === 'number') {
    return a * b;
  }
  else if (typeof a === 'string' && typeof b === 'string') {
    return a + b;
  }
  throw new Error('a和b必须是相同的类型');
}
const result = combine(34);
  1. 可选参数和默认值:可以在某些参数后加上问号,表示该参数可以不用传递
function sum (a: number, b: number, c?: number = 0) {
  if (c) {
   return a + b + c;
  } else {
    return a + b;
  }
};

demo:创建并打印扑克牌

目标:创建一副扑克牌(不包括大小王),打印该扑克牌;

type Deck = Card[];
type Color = "♠" | "♥" | "♣" | "♦";
type Card = {
  color: Color;
  mark: number;
};

function createDeck(): Deck {
  const deck: Deck = [];
  for (let i = 1; i <= 13; i++) {
    deck.push({
      mark: i,
      color: "♠"
    });

    deck.push({
      mark: i,
      color: "♣"
    });

    deck.push({
      mark: i,
      color: "♥"
    });

    deck.push({
      mark: i,
      color: "♦"
    });
  }
  return deck;
}

function printDeck(deck: Deck) {
  let deckStr = "";
  deck.forEach((el, i) => {
    if ((i + 1) % 6 === 0) {
      deckStr += el.color + el.mark + "\n";
    } else {
      deckStr += el.color + el.mark + "\t";
    }
  });

  console.log(deckStr);
}

printDeck(createDeck());

打印结果

结果
结果