更好的开发体验
在开发中解决 js 解决不了的问题
- 使用不存在的函数、变量、成员
- 类型错误:把一个不确定的类型,当作一个确定的类型使用
- 使用 undefined 或者 null 的成员
Uncaught TypeError: Cannot read property xxx of null
, 熟悉嘛? - ...
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
TypeScript
是JS
的一个超集,是一个可选的,静态的类型系统。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 会作出如下假设:
- 假设当前执行环境是 dom (浏览器环境)
- 代码中如果没有模块化语句(import、export),便认为此代码全局执行
- tsc 编译的目标代码是 es3
有两种方式更改上述默认设置:
- 使用 tsc 命令的时候加上参数
- 使用配置文件更改编译选项,配置文件之后,直接使用 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 类型作类型检查
基本类型约束
- number
- string
- boolean
- null 和 undefined
这两个比较特殊, 它俩不用作类型约束, 是所有类型的子类型,一个被定义为某个确定类型的变量可以被赋值为 null 和 undefined。
但是这样会引发问题:
let str:string = undefined;
str.toUpperCase() ????
是不是会出问题? 所以对于这个情况,一般是不允许这么做的,我们可以在编译选项中设置 严格的空类型检查
"strictNullChecks": true
复杂类型约束
- 数组
// 约束数组
let arr:number[];
let arr: Array[];
- 对象
let obj:object;
// obj 只能被赋值为一个对象,但是无法深入定制对象里面属性的类型
- 联合类型
有时候,比如一个用户的名字, 它可能是一个字符串,也可能还没有填写, 这时候是可以为 undefined 的,此时可以使用联合类型
let name:string | undefined = undefined;
当
ts
不确定类型的时候,你想象的这个类型对应的方法,不会给你智能提示。所以这里经过了判断,如果names 的是string
类型, 才会给出对应的智能提示, 因为代码执行, 如果进了这个判断, 这个names
一定是string
类型了。这也提醒我们, 如果在用 ts
开发过程中,你想使用某个方法却没有智能提示的时候, 就要小心了, 类型可能出问题了。
- void 类型: 通常用于约束函数的返回值, 表示不返回任何值。
funtion f():void{
return 1;
}
// 说一不二的 ts, 如果规定了此函数不返回值, 如果返回了也会报错。
- never : 通常用来约束函数的返回值, 表示这个函数永远不可能结束
函数结束的定义式最后一行代码执行完毕
function do():never{
while(true){}
}
- 字面量约束
这意思很清楚了,将变量a
以字符串“A”
约束,从此a只能是“A”
let sex: "男" | "女";
let user: {
name: string,
sex: "男" | "女",
age: number
}
// 注意 user 后面不是等号, 不是赋值, 这里是使用字面量的形式约束user
- 元组约束
var tuple: [string, number]
约束 tuple 必须是一个数组, 数组有且只能有两项, 第一项为字符串, 第二项为数字。
- 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
}
================================================
(前端知识博大精深,仅作为自己备忘,不作为教程 )