【问题标题】:TypeScript Type Annotation Excluding PrimitivesTypeScript 类型注释不包括基元
【发布时间】:2015-11-18 03:50:28
【问题描述】:

问题: 有什么方法可以在 TypeScript 中编写类型注释,允许任何对象字面量,但不允许内置类型 numberstringbooleanFunctionArray

为什么?

我一直在努力改进我在工作项目中使用的一些库的相关类型定义。我在许多 JS 库中注意到的一个常见模式是传递一个用于配置库或插件的“选​​项”对象。在这些情况下,您通常会看到类似这样的类型定义:

declare module 'myModule' {
    interface IMyModule {
        (opts?: IOptions): NodeJS.ReadWriteStream;
    }
    interface IOptions {
        callback?: Function;
        readonly?: boolean;
    }
}

这允许你像这样使用它:

var myModule = require('myModule');
myModule();
myModule({});
myModule({ callback: () => {} });
myModule({ readonly: false });
myModule({ callback: () => {}, readonly: false });

这些都是有效的用例。问题是这些类型定义也允许这些无效的用例:

myModule('hello');
myModule(false);
myModule(42);
myModule(() => {});
myModule([]);

在许多情况下,上述调用会导致运行时错误,因为库 JS 代码可能会尝试在对象上设置默认值,或者将选项传递给另一个库。尽管我尝试了很多事情,但我无法将参数限制为仅接受对象而不接受任何其他无效情况。

如果你有一个只有可选成员的接口(因为不需要任何一个选项),编译器会扩大可接受的类型以接受any

你可以在这里看到这个问题的 TypeScript Playground 演示:http://bit.ly/1Js7tLr

更新:不起作用的解决方案示例

interface IOptions {
    [name: string]: any;
    callback?: Function;
    readonly?: boolean;
}

此尝试要求对象上存在索引运算符,这意味着它现在抱怨numbers、strings、booleans、Functions 和Arrays。但这又带来了一个新问题:

var opts = {};
myModule(opts);

当它应该是一个有效的场景时,它现在失败了。 (在操场上看到这个:http://bit.ly/1MPbxfX

【问题讨论】:

  • 我看过这个:github.com/Microsoft/TypeScript/pull/3823,但我不确定这是否解决了这个问题,它是 v1.6。如果可能的话,我很想在 1.5 中找到一种方法来实现这一点。
  • 您可能只需要运行大量检查,我想这没什么好说的。所有这些案例都可能对您有所帮助:tobyho.com/2011/01/28/checking-types-in-javascript
  • @ZacharyDow,在这里我实际上并不是在编写 JavaScript 库,而是在编写描述现有 JavaScript 库 API 的 TypeScript 类型定义(请参阅 github.com/borisyankov/DefinitelyTyped)。我无法控制最终将被调用的代码。我的目标是帮助 TypeScript 用户在将错误类型作为选项传递时获得编译错误,因此他们不会错误地认为它会因为它编译而工作。如果没有这些编译时错误,用户就无法确信定义是正确或有用的。

标签: typescript definitelytyped


【解决方案1】:

从 TypeScript 2.2 开始,现在有一个 object 类型几乎与我上面描述的完全一样。

...除了stringbooleannumbersymbol 以及使用strictNullChecksnullundefined 时,您可以将任何内容分配给object 类型。

在此处查看官方公告:Announcing TypeScript 2.2

【讨论】:

    【解决方案2】:

    要使接口具有合理的强制执行力,它必须至少有一个强制成员。本质上,下面的接口什么都不强制:

    interface IOptions {
        callback?: Function;
        readonly?: boolean;
    }
    

    其实相当于“邪恶接口”:

    interface IOptions {
    }
    

    所以你得到了自动完成,但没有真正的类型检查。

    您提出的解决方案是可行的方法...

    interface IOptions {
        [name: string]: any;
        callback?: Function;
        readonly?: boolean;
    }
    

    在大多数情况下,人们倾向于在函数调用中构造这些选项,但如果他们真的想在其他地方这样做,他们可以使用类型断言:

    // If you must
    var opts = <IOptions>{};
    myModule(opts);
    
    // More typical
    myModule({});
    
    // Although... if its empty
    myModule();
    

    免责声明。其中一小部分只是我的观点……但是空接口是 TypeScript 中使用接口所能做的最糟糕的事情,而没有强制执行的接口则是第二糟糕的事情。

    您可以使用自己的基本接口来避免重复...

    interface Indexed {
        [name: string]: any;
    }
    

    甚至……

    interface Indexed<T> {
        [name: string]: T;
    }
    

    然后在你所有的选项界面上使用它...

    interface IOptions extends Indexed {
        callback?: Function;
        readonly?: boolean;
    }
    

    【讨论】:

    • 我认为你是对的,虽然我个人更喜欢 var opts: IOptions = {}; 而不是 var opts = &lt;IOptions&gt;{}; 我看到这些“空接口”遍布绝对类型,当我检查测试文件时,我看到了通过编译的东西但绝对不会工作。我希望有更好的方法,似乎必须将索引器添加到每个“选项”界面并不是最优雅的。感谢您确认。
    • 如果只有一个包含默认索引器的内置界面,您可以在界面中扩展它并默认获得正确的行为。
    • 如果你想在你的定义中包含基类,我已经以简单和通用的形式添加了它:)
    猜你喜欢
    • 2018-06-10
    • 2016-03-31
    • 1970-01-01
    • 2021-12-22
    • 2012-10-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多