如何使用 TypeScript 键入类似 jQuery 的初始化模式?



我在我的库中使用了类似jQuery的初始化模式(我添加了一些关于我希望这些函数接受和返回的类型的评论(:

function JQueryConstructor ( selector /*: string | INSTANCE_OF_JQUERY*/ ) {
if ( selector.__jquery ) return selector;
}
function jQuery ( selector/*: string | INSTANCE_OF_JQUERY*/ )/*: INSTANCE_OF_JQUERY*/ {
return new JQueryConstructor ( selector );
}
const fn = jQuery.fn = jQuery.prototype = JQueryConstructor.prototype = {
constructor: jQuery,
__jquery: true
};
fn.foo = function () {};
jQuery ( '*' ).foo; // => Function
const $mocked = { __jquery: true, foo () {} };
jQuery ( $mocked ) === $mocked; // => true

它在 JavaScript 中工作,但是我如何在 TypeScript 中编写它以便正确键入它?

我有以下要求/问题:

  • 我不想写一个单独的声明文件(jQuery就是这种情况(,它必须由TypeScript生成
  • TypeScript 编译器输出的 JavaScript 在缩小后应该具有基本相同的大小,大小对我的库很重要
  • TypeScript 应该是 JS 的超集,但看起来我可以从 JavaScript 中称为new Foo ()的函数返回任意值,而 TypeScript 抱怨只有 void 函数可以这样调用
  • 我需要能够扩展原型并生成正确的声明文件

我找不到解决所有这些问题的方法,你能帮我吗?

为了解决您的要求/问题,请按顺序:

  • 如果我们的代码Typescript符合"declaration": true选项,我们可以让编译器这样做
  • 有一个很小的例子,但是一旦你正确处理了管道,JS TS 生成的大小应该与 JS 大致相同
  • Typescript 是 JS 的超集,因为任何有效的 JS 在语法上都是正确的 TS。打字稿确实执行额外的语义检查。其中一项检查是不允许使用new调用任何函数,或者不允许缺少匹配类型。尽管如果您有语义错误(默认情况下编译器会这样做(,您仍然可以发出 JS,但建议您修复这些错误。
  • Typescript 允许接口和接口、接口和类、类和命名空间之间的各种合并,所有这些都是为了帮助实现类型安全的可扩展性。您可以在此处阅读更多内容

我对您想要的解决方案是使用类而不是函数JQueryConstructor并使用类接口合并来实现可扩展性。结果将如下所示:

class JQuery{
__jquery!: boolean; // Just the declaration
constructor(selector: string | JQuery)  {
if(typeof selector !== 'string' && selector.__jquery) return selector;
}
}
JQuery.prototype.__jquery = true;
function foo() { };
interface JQuery {
foo: typeof foo
}
JQuery.prototype.foo = foo;

function jQuery(selector: string | JQuery): JQuery {
return new JQuery(selector);
}

jQuery('*').foo; // => Function
const $mocked = { __jquery: true, foo() { } };
jQuery($mocked) === $mocked; // => true

代码的缩小版本(此处缩小(与您的版本没有太大区别,您的版本是 300 字节,我编译为 es2015 的版本是 261 字节,编译为 es5 的版本是 253 字节(即使我使用您更长的JQueryConstructor名称,它仍然是 297 字节(。在这样一个小例子上比较大小可能很相关,但它们似乎是可比的。

基于上述代码,生成以下定义:

declare class JQuery {
__jquery: boolean;
constructor(selector: string | JQuery);
}
declare function foo(): void;
interface JQuery {
foo: typeof foo;
}
declare function jQuery(selector: string | JQuery): JQuery;
declare const $mocked: {
__jquery: boolean;
foo(): void;
};

这将适用于任何消费者,并允许他们使用相同的类接口合并技术来提供可扩展性。

多亏了@titian-cernicova-dragomir,我想出了以下解决方案,由于必须显式定义所有接口,它有点冗长,但它有效:

interface jQuery {
constructor: typeof jQueryConstructor,
__jquery: boolean
}
function isJQuery ( x ): x is jQuery {
return x && x['__jquery'];
}
function jQuery ( selector: string | jQuery ): jQuery {
if ( isJQuery ( selector ) ) return selector;
return new jQueryConstructor ( selector );
}
function jQueryConstructor ( selector: string ) {}
const fn = jQuery.fn = jQuery.prototype = jQueryConstructor.prototype = {
constructor: jQueryConstructor,
__jquery: true
} as jQuery;

interface jQuery {
foo ();
}
fn.foo = function () {};
jQuery ( '*' ).foo; // => Function
const $mocked = { __jquery: true, foo () {} } as jQuery;
jQuery ( $mocked ) === $mocked; // => true
jQuery ( '*' ) instanceof jQuery; // => true

最新更新