排除基元的 TypeScript 类型注释

TypeScript Type Annotation Excluding Primitives

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

为什么?

我一直致力于为我在工作项目中使用的一些库改进 DefinitelyTyped 上的一些类型定义。我在许多 JS 库中注意到的一个常见模式是传递用于配置库或插件的 'options' 对象。在这些情况下,您经常会看到类似这样的类型定义:

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);

现在这应该是一个有效的场景却失败了。 (在 Playground 中查看:http://bit.ly/1MPbxfX

要使接口具有合理的执行,它必须至少有一个强制成员。本质上,下面的接口不执行任何操作:

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

实际上相当于"evil interface":

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;
}

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

... you can assign anything to the object type except for string, boolean, number, symbol, and, when using strictNullChecks, null and undefined.

在这里查看官方公告:Announcing TypeScript 2.2