使用Typescript给JavaScript做静态类型检查

栏目: JavaScript · 发布时间: 6年前

内容简介:笔者所说的这个项目,是一个运行了接近五年的老项目,代码横跨es3到es6。而且代码风格由于项目的人员流动也各异。由于项目人数越来越多,以前留下的技术债造成的危害也越来越大。而重构,在这样一个臃肿且混乱的项目中显得举步维艰。由于JS的动态性,有的时候你甚至不知道这个方法是某个类下的实例方法还是JavaScript原生或是JQuery所提供的方法。 这个时候,TypeScript由于其提供的强类型能很好的规范代码,方便开发人员进行日常开发和重构,开始进入了我们的视野。 但是马上我们面对上了另一个难题,那就是,

笔者所说的这个项目,是一个运行了接近五年的老项目,代码横跨es3到es6。而且代码风格由于项目的人员流动也各异。由于项目人数越来越多,以前留下的技术债造成的危害也越来越大。而重构,在这样一个臃肿且混乱的项目中显得举步维艰。由于JS的动态性,有的时候你甚至不知道这个方法是某个类下的实例方法还是JavaScript原生或是JQuery所提供的方法。 这个时候,TypeScript由于其提供的强类型能很好的规范代码,方便开发人员进行日常开发和重构,开始进入了我们的视野。 但是马上我们面对上了另一个难题,那就是,我们只想享受TypeScript强类型带来的类型推导的优势,并不想花大功夫去把整个代码库全部重构成TypeScript。 不过好在,TypeScript从2.3版本后就开始支持使用JsDoc的语法,以comment的形式给JavaScript文件提供强类型的支持。 Type Checking JavaScript Files · TypeScript

如何开始使用Type Checking

首先你需要在你需要检查的JavaScript的文件头部显式的加上如下comment

// @ts-check
复制代码

对变量进行类型检查

如果你需要声名一个变量的类型,你可以这样

/** @type {number} */
let x;

x = 0;      // OK
x = false;  // Error: boolean is not assignable to number
复制代码

当你尝试给x赋一个bool值时,编辑器会提示你类型冲突。

对函数进行类型检查

当然某些时候我们最需要的其实是对于函数签名和返回值的强类型声名,以便我们在其他地方使用的时候不会传入错误的参数或者使用类型错误的函数返回值。TypeScript提供了三种语法来声名函数的类型.

// jsdoc standard syntax
// 声明一个函数
/** 
 * @param {string} foo 
 * @param {string} bar
 * @returns {string}
*/

function test(foo, bar) {
  return `${foo} and ${bar}`
}

// closure syntax
// 声明一个函数表达式
/** 
 * @type {function(string, string): string}
*/

let test; // test 必须符合定义的函数签名
test = (foo, bar, foobar) => '123' // 报错 函数不符合定义的签名
test = (foo, bar) => 1  // 报错 函数返回值不符合


// typescript like syntax
// 声明一个函数表达式
/** 
 * @type {(foo:string, bar:string) => string}
*/

let test; // test 必须符合定义的函数签名
复制代码

在日常使用中,如果你想声明一个函数,对于函数做类型检查,你需要使用 @params @returns 这种declaration 语法。 如果你想确定一个函数表达式的签名,你需要使用 @type 的语法。

对于 @type 语法, 从表现力上,我个人更偏好 typscript like syntax 因为表达力最强。但是可惜的是这种语法在webstorm上会被认为是非法语法。

自定义类型

自定义类型有点类似于定义TypeScript中的Interface,TypeScript同样支持两种语法来自定义类型。

/** 
 * @typedef {Object} Human
 * @prop {string} name
 * @property {number} age
 * @prop {(string) => void} talk
 */

/** @type {Human} */
 let Human;

/** 
 * @typedef {{name: string, age:number, talk: (string) => void}} Person
 */

/** @type {Person}*/
let person;
复制代码

需要注意的是,第二种语法在webstorm中也不被认为只一个合法的语法。

声明一个class

对于类的property的类型,可以很轻松的进行检查

// 有初始化值,可以依赖类型推导
class C {
    constructor() {
        this.constructorOnly = 0
        this.constructorUnknown = undefined
    }
    method() {
        this.constructorOnly = false // error, constructorOnly is a number
        this.constructorUnknown = "plunkbat" // ok, constructorUnknown is string | undefined
        this.methodOnly = 'ok'  // ok, but y could also be undefined
    }
    method2() {
        this.methodOnly = true  // also, ok, y's type is string | boolean | undefined
    }
}

// 无初始化值,显式声明.
class C {
    constructor() {
        /** @type {number | undefined} */
        this.prop = undefined;
        /** @type {number | undefined} */
        this.count;
    }
}

let c = new C();
c.prop = 0;          // OK
c.count = "string";  // Error: string is not assignable to number|undefined

复制代码

对于类的实例方法,你需要对于函数做出声明.

class Foo {
  /**
   * @param {string} binggo
   * @returns {string} 
  */
  bar(bingo) {
    return bingo;
  }
}

let test2 = new Foo()
test2.bar(1); // Error Emit.
复制代码

其他的一些特性

可选参数

/**
 * @param {string} [somebody] - Somebody's name.
 */
function sayHello(somebody) {
    if (!somebody) {
        somebody = 'John Doe';
    }
    console.log('Hello ' + somebody);
}

sayHello();
复制代码

类型union

/**
 * @type {(string | boolean)}
 */
var sb;
复制代码

三方类型

三方类型在这里既可以是 npm提供的 @types 包,也可以是自己在项目中通过TypeScript定义的类型。 只要在typeRoots这个编译器选项中指明即可。

如何和项目做集成

IDE和编辑器集成

项目以使用vscode和webstorm为主,但是理论上,只要能支持 TypeScript Language Server Protocal 的编辑器都不会出现集成上的问题。

vscode

对于vscode,不需要做过多的配置,直接在文件头部加入 // @ts-check 的comment就可以开启TypeScript checking.

WebStorm

对于WebStorm,你需要提供对应的 tsconfig.json 文件来显式的打开这个特性。

{
    "compilerOptions": {
        "allowJs": "true", // 开启js checking
        "noEmit": "true",  // 不生成输出文件
        "target": "es6",    // 支持es6语法
        "checkJs": "true",  // 打开会检查所有的js, 如果不需要可以手动的在需要检查的js文件头部加上 @ts-check的标记.
    },
    "include": [
        "src/**/*"
    ],
    "exclude": [
        "node_modules",
        "**/*.spec.ts"
    ]
}
复制代码

工作流集成

除开IDE和编辑器等开发 工具 的集成之外,我们还需要把TypeScript Checking JavaScript集成进我们的构建流程之中。TypeScript的CLI提供了非常强大的功能,由于在 tsconfig.json 中声名了只做类型检查,不做实际的编译,所以通过一条简单的 tsc 命令就能很好的帮助我们在构建中检查我们的代码是否存在构建错误。

一些限制

凡事都有两面性,Type Checking JS也并非没有缺点。在一段时间的试验后,我发现和直接编写TypeScript相比,Type Checking JS存在以下不爽的地方

1. 使用comments破坏代码语义化

比起类型声明和代码结合在一起,使用comments的形式难免在阅读和编写的时候感觉有些别扭,在代码上也有一定的冗余。

// ts 写法
let foo:string = (bar: string) => `${bar}`

// type checking js写法
/** @type {(bar: string) => string}
let foo = bar => `${bar}`
复制代码

2. 语法支持不统一

Webstorm出于某种原因并不支持typescript like的语法,我们只能退而求其次选择closure like的语法。 这让Type Checking JS的写法下降了不止一个档次。


以上所述就是小编给大家介绍的《使用Typescript给JavaScript做静态类型检查》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Release It!

Release It!

Michael T. Nygard / Pragmatic Bookshelf / 2007-03-30 / USD 34.95

“Feature complete” is not the same as “production ready.” Whether it’s in Java, .NET, or Ruby on Rails, getting your application ready to ship is only half the battle. Did you design your system to......一起来看看 《Release It!》 这本书的介绍吧!

图片转BASE64编码
图片转BASE64编码

在线图片转Base64编码工具

SHA 加密
SHA 加密

SHA 加密工具

HEX CMYK 转换工具
HEX CMYK 转换工具

HEX CMYK 互转工具