第159期:对typescript的一些理解

173 阅读5分钟

封面图

image.png

下午没事的时候拆了一支玩具枪,按照我之前的理解,里面应该是有一支撞针,但是拆开之后看到里面其实是用的压缩空气来发射子弹。白色的部分其实是类似一个气筒,压缩之后通过前面的枪管将子弹发射出去,如果枪身都改用金属,同时增强弹簧的弹力,威力应该能提高不少。

关于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的使用规范。

比如:

  • 不要使用如下类型NumberStringBooleanObject。 这些类型指的是非原始的装盒对象,它们几乎没在JavaScript代码里正确地使用过。

    /* 错误 */ 
    function reverse(s: String): String;
    

应该使用类型numberstring,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是一种语法而已,无非是声明变量、函数、类型的时候需要同时也声明一下对应的类型。但是写的越多,越觉得它其实就是一种规范,我们按照这种规范去写,代码的可读性慢慢的变得好起来了,编译时又能将错误抛出来,其实是一举两得。

当然,不同的团队有不同的规范,但是最根本的其实还是文档里讲的那些内容。

最后,你当前的网站能够给你带来足够的点击量,并且将这些点击量有效的转化为客户吗?贵公司可能和大多数小企业一样,答案是【不能】。不过不用担心,我可以帮你创建一个顶级的定制化网站,专门来提高你的流量和转化率。我已经帮许多其他的小企业提高来两倍甚至三倍的客户,我也可以帮你做到。

当然,我的能力也不限于仅仅定制网站,微信相关的小程序应用、公众号应用、企业微信等等,我都可以帮你做定制,当然,客户端开发也可以。

需要的话可以联系我~