Typescript - why-what-how?

更好的开发体验
在开发中解决 js 解决不了的问题

  1. 使用不存在的函数、变量、成员
  2. 类型错误:把一个不确定的类型,当作一个确定的类型使用
  3. 使用 undefined 或者 null 的成员 Uncaught TypeError: Cannot read property xxx of null, 熟悉嘛?
  4. ...

JavaScript

  • 弱类型:变量可以随时更改类型。
var a;
var a = 10;
var a = 'string';

在声明变量的时候, 我们就没有给变量 a 一个确定的类型,所以在开发中,a 的类型可以任意改变,而事实上真的需要这样做吗(或者说允许一个变量可以随意更改一个类型真的是好事吗)?

var width = 100;
// 省略三千行代码
width = '200px';
//再省略三千行代码
document.getElementById('xxx').style.width = ???

我们在投入业务开发中的时候, 更多的关注点在于业务,而不是语言特性,所以在某个时间节点,我也不知道width 被重新赋值了多少次,我现在想用width 这个变量的时候,width 究竟是个什么?数字还是字符串? 要不要加上"px", 不回去看代码九成不知道。

如果有一个东西,能够在我第二次将 width 变成一个字符串的时候, 提醒我, 你不能这么干, 这个width原来是一个数字,你写成了字符串,后面会出现混乱!

  • 解释型语言:错误发生在运行时。
    代码运行是解释一行执行一行,没有传统语言的编译过程,所以无法在执行之前发现错误, 但是写代码的是人,人是不可能避免出现错误的。
  • Javascript本身的语言特性, 它并不适合做大型项目。

所以在前端开发中,大部分时间都在排错😂。

TypeScript

TypeScriptJS 的一个超集,是一个可选的,静态的类型系统。TS 不是一门新的语言,js 代码修改后缀为 ts 一样可以正常执行,超集的意思是: 男人、女人、小孩、大人,人是超集。只要是人, 都在这个集合里面。

TypeScript 可以解决上述问题。它会对代码中的所有标识符(变量、函数、参数、返回值等)进行类型检查和推导。从而减少不必要的错误发生,换言之,开发过程中使用 ts比 使用js拥有更强的生产力。

2012 年由微软发布并开源。

TS 代码不能直接运行在各个环境中

TS 增加了一些特殊的语法来进行类型检查,所以,无论是浏览器还是Node 环境,都不能识别这些新增加的语法。需要通过 TSC (TypeScript Compiler )编译之后执行。编译之后是纯粹的Javascript代码

开始吧

全局安装 typescript 和 编译器 tsc,新建一个 ts 文件, 写两行代码:



现在你还会因为类型错误浪费大量时间查错而烦恼吗?

function getNumber():number{
  return 1
}
var width:number = 100;

再写两行代码, ts 与 js 明显的不同就是,定义了变量、函数返回值的类型。如果返回的类型不是期望的类型,编辑器首先会提示报错,如果对此置之不理,使用 tsc 编译的时候会报错。这就和强类型语言很像了,语法不对,编译都不能通过。

function getNumber() {
    return 1;
}
var width = 100;

使用tsc src/index/ts编译之后的代码与正常的 js 一样。

注意
默认情况下, ts 会作出如下假设:

  1. 假设当前执行环境是 dom (浏览器环境)
  2. 代码中如果没有模块化语句(import、export),便认为此代码全局执行
  3. tsc 编译的目标代码是 es3

有两种方式更改上述默认设置:

  1. 使用 tsc 命令的时候加上参数
  2. 使用配置文件更改编译选项,配置文件之后,直接使用 tsc 就可以了, 如果后面指定了文件夹,那么会忽略掉配置文件。

对变量以及函数的约束

// 约束变量
let name:string = "mike";
let age:number = 18;

// 约束函数
function add (a:number, b:number):number{
  return a + b;
}
// 参数以及返回值的约束,如果传参和返回值类型不符合约束,会直接提示报错
let number:number = add(1,2);

在很多场景下, ts 可以自行完成对某些变量类型的推导:

function add (a:number, b:number){
  return a + b;
}

此函数返回的一定是一个number, 这是确定的:



在每一个声明,赋值的地方,都会有类型检查。

let a; // 这是一个 any 类型, 即任意类型, ts 不对 any 类型作类型检查

基本类型约束

  1. number
  2. string
  3. boolean
  4. null 和 undefined

这两个比较特殊, 它俩不用作类型约束, 是所有类型的子类型,一个被定义为某个确定类型的变量可以被赋值为 null 和 undefined。
但是这样会引发问题:

let str:string = undefined;
str.toUpperCase() ????

是不是会出问题? 所以对于这个情况,一般是不允许这么做的,我们可以在编译选项中设置 严格的空类型检查 "strictNullChecks": true

复杂类型约束

  1. 数组
// 约束数组
let arr:number[];
let arr: Array<number>[];
  1. 对象
let obj:object;
// obj 只能被赋值为一个对象,但是无法深入定制对象里面属性的类型
  1. 联合类型
    有时候,比如一个用户的名字, 它可能是一个字符串,也可能还没有填写, 这时候是可以为 undefined 的,此时可以使用联合类型
let name:string | undefined = undefined;

ts 不确定类型的时候,你想象的这个类型对应的方法,不会给你智能提示。所以这里经过了判断,如果names 的是 string 类型, 才会给出对应的智能提示, 因为代码执行, 如果进了这个判断, 这个 names 一定是 string类型了。这也提醒我们, 如果在用 ts 开发过程中,你想使用某个方法却没有智能提示的时候, 就要小心了, 类型可能出问题了。
  1. void 类型: 通常用于约束函数的返回值, 表示不返回任何值。
funtion f():void{
  return 1;
}
// 说一不二的 ts, 如果规定了此函数不返回值, 如果返回了也会报错。
  1. never : 通常用来约束函数的返回值, 表示这个函数永远不可能结束
    函数结束的定义式最后一行代码执行完毕
function do():never{
  while(true){}
}
  1. 字面量约束

    这意思很清楚了,将变量a以字符串“A”约束,从此a只能是“A”
let sex: "男" | "女";

let user: {
  name: string,
  sex: "男" | "女",
  age: number
}
// 注意 user 后面不是等号, 不是赋值, 这里是使用字面量的形式约束user
  1. 元组约束
var tuple: [string, number]

约束 tuple 必须是一个数组, 数组有且只能有两项, 第一项为字符串, 第二项为数字。

  1. any: 此类型可绕过类型检查。可以赋值为任意类型。

类型别名

 type User =  {
  name: string,
  sex: 'male' | 'female',
  age: number
}
// 函数返回一个 User 类型的数组。
function getUsers ():User[]{
  return []
}

函数相关约束

这里有个函数, 接收两个参数。 如果这两个参数都是数字, 返回二者的乘积, 如果二者都是字符串, 拼接返回。

function combine (a: number|string, b: number|string):number|string{
  if(typeof a === 'number' && typeof b === 'number'){
    return a * b;
  }
  if(typeof a === 'string' && typeof b === 'string'){
    return a + b;
  }
  throw "a and b should be a same type";
}
let res = combine(1, 2)

我传入了两个数字,很明显返回值肯定是一个 number, 但是系统提示res 可能是 string 或者 number。 这是不符合我们要求的。 我们希望, 传两个相同类型的值做出对应的确定的回应。
function combine(a: number, b:number):number;
function combine(a: string, b:string):string;
function combine (a: number|string, b:number|string) :number|string{
  if(typeof a === 'number' && typeof b === 'number'){
    return a * b;
  }
  if(typeof a === 'string' && typeof b === 'string'){
    return a + b;
  }
  throw "a and b should be a same type";
}

let res = combine(1, 2)

这下确定了!我们使用声明式的约束,明确指出,同number返回number。这类似于 Java 的重载。

可选参数 & 默认参数

function sum(a: number, b: number = 0, c?: number){
  return c ? a + b + c : a + b;
}

在参数名之后,添加一个 ?,表示该参数可选。
在参数声明完成后, 可以为参数添加默认值:b: number = 0

枚举

enum Gender {
  male = "男",
  female = "女"
  // 逻辑名称 = 真实值 
}
  • 数字类型的枚举,默认会自增,如果不赋值, 默认从0开始自增。
enum Level {
    normal = 1,
    vip, // 2
    svip, // 3
    s_svip // 4
}

================================================

(前端知识博大精深,仅作为自己备忘,不作为教程 )

全部评论

相关推荐

点赞 收藏 评论
分享
牛客网
牛客企业服务