关于TypeScript
关于TypeScript,我之前对它的理解现在想来还是太过于流于表面了。之前一直觉得它不过是提供了一种写法,然后我们按照它的写法,代码运行起来可以在编译时将代码中的错误抛出来,对于普通的项目,如果我们仅仅是从语法上从js 改为ts ,那么其实没什么必要。
这是我之前对TypeScript的一些粗浅的见解。
但是当我在项目中不断的使用TypeScript之后,我忽然发现我们不能仅仅将TypeScript作为一种语言,或者一种js的语法,它其实更接近一种规范,或者说它本身就是一种规范。
为什么这么说呢?如果我们不用TypeScript进行开发,那么我们的代码通常都是直接用js进行写,定义变量,或者函数接收参数的时候,我们都需要做各种类型的判断。但是如果我们换做TypeScript,那么定义变量的时候我们会直接定义好类型,函数接收什么样类型的参数,返回什么样类型的数据,都是需要事先考虑清楚的。否则,代码在编译时有可能就会报各种各样的错误出来。
TypeScript的手册中告诉我们如何去声明一个变量,如何声明一个接口:
interface LabelledValue {
label: string;
}
function printLabel(labelledObj: LabelledValue) {
console.log(labelledObj.label);
}
let myObj = {size: 10, label: "Size 10 Object"};
printLabel(myObj);
定义可选属性:
interface SquareConfig {
color?: string;
width?: number;
}
索引类型:
interface StringArray {
[index: number]: string;
}
枚举:
enum Direction {
Up = 1,
Down,
Left,
Right
}
如上,我们定义了一个数字枚举, Up
使用初始化为 1
。 其余的成员会从 1
开始自动增长。 换句话说, Direction.Up
的值为 1
, Down
为 2
, Left
为 3
, Right
为 4
。
泛型:
function identity<T>(arg: T): T {
return arg;
}
除此之外,还有比如联合类型,交叉类型,可以为 null
的类型等等如何定义的例子。
规范
手册中除了告诉我们如何去定义这些变量、类型之外,其实还有一节非常重要的内容讲的是TypeScript的使用规范。
比如:
- 不要使用如下类型
Number
,String
,Boolean
或Object
。 这些类型指的是非原始的装盒对象,它们几乎没在JavaScript代码里正确地使用过。
/* 错误 */
function reverse(s: String): String;
应该使用类型number
,string
,and boolean
。
- 不要定义一个从来没使用过其类型参数的泛型类型。
- 不要为返回值被忽略的回调函数设置一个
any
类型的返回值类型:
/* 错误 */
function fn(x: () => any) { x(); }
- 应该给返回值被忽略的回调函数设置
void
类型的返回值类型:
/* OK */
function fn(x: () => void) { x(); }
因为使用void
相对安全,因为它防止了你不小心使用x
的返回值:
function fn(x: () => void) {
var k = x(); // oops! meant to do something else
k.doSomething(); // error, but would be OK if the return type had been 'any'
}
这也是在我们开发的项目中为什么禁止使用any
声明变量的原因之一,因为使用any
有可能会将很多错误遗漏掉,或者代码运行时无法检查出一些问题,所以我们声明变量及参数时,都是需要明确变量及参数的类型。
- 不要在回调函数里使用可选参数除非你真的要这么做:
/* 错误 */
interface Fetcher {
getObject(done: (data: any, elapsedTime?: number) => void): void;
}
这里有一种特殊的意义:done
回调函数可能以1个参数或2个参数调用。 代码大概的意思是说这个回调函数不在乎是否有 elapsedTime
参数, 但是不需要把这个参数当成可选参数来达到此目的 -- 因为总是允许提供一个接收较少参数的回调函数。
- 不要因为回调函数参数个数不同而写不同的重载:
/* 错误 */
declare function beforeAll(action: () => void, timeout?: number): void;
declare function beforeAll(action: (done: DoneFn) => void, timeout?: number): void;
应该只使用最大参数个数写一个重载:
/* OK */
declare function beforeAll(action: (done: DoneFn) => void, timeout?: number): void;
因为回调函数总是可以忽略某个参数的,因此没必要为参数少的情况写重载。 参数少的回调函数首先允许错误类型的函数被传入,因为它们匹配第一个重载。
最后
原先我觉得TypeScript是一种语法而已,无非是声明变量、函数、类型的时候需要同时也声明一下对应的类型。但是写的越多,越觉得它其实就是一种规范,我们按照这种规范去写,代码的可读性慢慢的变得好起来了,编译时又能将错误抛出来,其实是一举两得。
当然,不同的团队有不同的规范,但是最根本的其实还是文档里讲的那些内容。