类型系统不止 TypeScript 有,别的语言 Java、C++ 等都有,为什么 TypeScript 的类型编程被叫做类型体操,而其他语言没有呢?这节主要分析这个问题。
TypeScript 给 JavaScript 增加了一套静态类型系统,通过 TS Compiler 编译为 JS,编译的过程做类型检查。
它并没有改变 JavaScript 的语法,只是在 JS 的基础上添加了类型语法,所以被叫做 JavaScript 的超集。
JavaScript 的标准在不断的发展,TypeScript 的类型系统也在不断完善,因为“超集”的设计理念,这两者可以很好的融合在一起,是不会有冲突的。
静态类型编程语言都有自己的类型系统,从简单到复杂可以分为 3 类:
简单类型系统
变量、函数、类等都可以声明类型,编译器会基于声明的类型做类型检查,类型不匹配时会报错。
下面最基础的类型系统,能保证类型安全,但有些死板。比如一个 add 函数既可以做整数加法、又可以做浮点数加法,却需要声明两个函数:
int add(int a, int b) {
return a + b;
}
double add(double a, double b) {
return a + b;
}
可以看出这个类型系统极为死板,接下来看看第二种类型系统
支持泛型的类型系统
泛型的英文是 Generic Type,通用的类型,它可以代表任何一种类型,也叫做类型参数
。有泛型的加入,给类型系统增加了灵活性,在整体固定,部分变量类型有变化的情况下可以有效减少重复代码,例如:
T add<T>(T a, T b){
return a + b;
}
声明时把会变化的类型声明成泛型(也就是类型参数),在调用的时候再确定类型。Java 就是这种类型系统。但是这种类型系统的灵活度对于JavaScript来说还不够,例如在java中对象都是由类new出来的而js支持对象字面量,那么在一个返回对象的某个属性值的函数中:
function getPropValue<T>(obj: T, key): key对应的属性值类型 {
return obj[key];
}
拿到了T,也拿不到对象的属性和属性值。
支持类型编程的类型系统
在 JavaScript 里面,对象可以字面量的方式创建,还可以灵活的增删属性,拿到对象并不能确定什么,所以要支持对传入的类型参数做进一步的处理。
对传入的类型参数(泛型)做各种逻辑运算,产生新的类型,这就是类型编程。
修改上面的函数代码:
function getPropValue<
T extends object,
Key extends keyof T
>(obj: T, ket: Key):T[Key]{
return obj[key]
}
这里的 keyof T、T[Key] 就是对类型参数 T 的类型运算。
TypeScript 的类型系统就是第三种,支持对类型参数做各种逻辑处理,可以写很复杂的类型逻辑。
类型逻辑是对类型参数的各种处理,可以实现很多强大的功能,例如:
type res = {
a:"1";
c:"3";
b:"2";
}
type res = ParseQueryString<'a=1&b=2&c=3'>
它可以对传入的字符串的类型参数做解析,返回解析以后的结果。 TypeScript 的类型系统可以对泛型做各种逻辑处理。
TypeScript 的类型系统是图灵完备
的,也就是能描述各种可计算逻辑。简单点来理解就是循环、条件等各种 JS 里面有的语法它都有,JS 能写的逻辑它都能写。
对类型参数的编程是 TypeScript 类型系统最强大的部分,可以实现各种复杂的类型计算逻辑,是它的优点。但同时也被认为是它的缺点,因为除了业务逻辑外还要写很多类型逻辑。因此便有了类型体操