将过时的JavaScript库与更新的TypeScript集成

Integrating dated JavaScript libraries with updated TypeScript

本文关键字:更新 TypeScript 集成 过时 JavaScript      更新时间:2023-09-26

我和我的团队正在构建一个大型web应用程序项目,该项目使用流行JavaScript库(jQuery、Backbone、Undercore等)的旧版本。我们目前正在尝试逐步将TypeScript用于新的解决方案以及替换现有的JavaScript代码。因为我们的项目在很大程度上依赖于特定的JS库版本(例如jQuery 1.8.3),所以将这些库更新到最新版本比下载并放入最新版本稍微复杂一些。

这给我们带来了一个分阶段使用TypeScript的问题;我们需要与最新TypeScript标准兼容的过时JS库版本的声明。尽管在为最新版本的JS库,特定于最近版的TypeScript,它不提供与最新TypeScript标准兼容的JS库版本的声明文件。

似乎随着TypeScript的发展,所有旧的库声明(例如jQuery-1.8.3.d.ts)都被抛在了脑后,因为这些库正在同时发展。这是完全可以理解的,因为我不希望社区中有太多的努力来不断更新过时的库声明文件,以便它们与新的TypeScript标准兼容。

所以我的问题是:对于严重依赖旧JS库的新TypeScript代码,分阶段使用的最佳方法是什么

下面是我遇到的一个问题的例子。我本质上是在尝试使用TypeScript创建一个Backbone应用程序。

// Model.ts 
import Backbone = require("backbone");
export class NewModel extends Backbone.Model {
    ...
}

因为我们使用的是带有require.js的AMD,所以我们得到了如下内容:

// Model.js
...
define(['require', 'exports', 'backbone'], function(require, exports, Backbone) {
...
}

这很好,正是我们想要的输出,因为我们的require.config文件定义了我们需要的所有库:

// Config.ts
require.config({
    baseUrl: './',
    paths: {
        'jquery': '../jquery-1.8.3.min',
        'underscore': '../underscore/underscore.min',
        'backbone': '../backbone/backbone',
        'text': '../require/text'
    },
    shim: {
        jquery: {
            exports: '$'
        },
            underscore: {
            exports: '_'
        },
        backbone: {
            deps: ['underscore', 'jquery'],
            exports: 'Backbone'
        }
    }
});

但是,TypeScript不允许我们编译此代码,除非我们有一个主干.d.ts,它描述了我们的骨干.js库的结构,并提供了"主干"模块的环境声明

// backbone.d.ts
declare module Backbone {
    ...
    export class Model extends ModelBase {
        ...
    }
    ...
}
declare module 'backbone' {
    export = Backbone;
}
// Model.ts
/// <reference path="path/to/backbone.d.ts" /> 
import Backbone = require("backbone")
export class NewModel extends Backbone.Model {
    ...
}

如果我们没有这些声明,那么我们就没有主干模块的环境声明,因此不能在TypeScript中使用extend Backbone.Model,因为Backbone.Model在任何地方都没有定义为类。这是一个有点夸张的示例问题,因为无论版本如何,Model类类型都存在于所有"主干.d.ts"文件中。然而,如果没有一个准确的.d.ts文件来表示代码,JS库还有一些更微妙的变化,可能会导致代码中断。

话虽如此,只要你只使用相关JS库中实际存在的部分,并且直接使用它们,你也可以使用更新版本的.d.ts文件(我们一直在做)。否则,如果您使用了一个不存在的函数,或者如果您传递了太多参数,那么只有在实际调用该函数时才会看到该错误。TypeScript不会接受它,因为它的信息完全基于.d.ts文件的信息。

我们可以使用amd依赖性工作来生成define需求,比如:

// Model.ts 
/// <amd-dependency path="backbone" />
var Backbone = require("backbone");
export class NewModel extends Backbone.Model {
    ...
}

这将生成以下内容:

// Model.js
...
define(['require', 'exports', 'backbone'], function(require, exports) {
var Backbone = require('backbone');
...
}

这给了我们想要的Backbone引用,但TypeScript仍然会抱怨,因为它不知道我们正在声明的Backbone变量。换句话说,它没有提供.d.ts文件提供的结构。

尽管我们在.js文件和相关的.d.ts文件之间缺乏相关性,但我们的大多数TypeScript解决方案实际上都能正常工作。我担心的是:将来这种差异会在我们的应用程序中引起重大问题吗

现在,我能想到的最好的解决方案是:

  1. 获取最接近我们JS库版本的.d.ts版本,并对其进行更新,使其与最新的TypeScript标准兼容
  2. 使用最新的.d.ts文件,并在遇到问题时相应地进行更新

我对这两种解决方案都不是特别兴奋,这就是为什么我要寻求另一种意见。如果有人在这一点上仍然和我在一起(很抱歉拖延),那么我将感谢你提出的解决方案。

这可能是一个粗略的问题,我们的团队在TypeScript的早期肯定会处理这个问题。最终,我们从绝对类型上的d.ts文件版本开始,并根据我们的需要对其进行调整。对于许多库,我们发现键入没有使用新的语言功能,不完整,或者在某些情况下完全错误。我们已经尽了最大努力,在有意义的地方做出贡献。

考虑到你正在考虑的两种方法,我会选择#1。当语言以极快的速度发展时,让d.ts文件跟上最新的TypeScript标准更是一个问题。虽然我仍然预计v2会有很多新功能,但v1相当稳定。在1.0之前的运行过程中,语言似乎每月都会发生变化。我认为我们现在不需要处理这种程度的流失:)