打字稿功能/对象参数

use*_*362 5 javascript typescript

为什么打字稿 ES6 没有检测到对象不是函数?

find: (collection: string, query: object, sortQuery = {}, cb?: Function)  => {
    socketManager.call('find', collection, query, sortQuery, cb);
}
Run Code Online (Sandbox Code Playgroud)

基于这个函数,你会假设这会失败:

this._services._socket.methods.find('vendors', {type: 'repair'}, (errVen, resVen) => {}
Run Code Online (Sandbox Code Playgroud)

由于没有 sortQuery 对象,而是一个回调函数。这没有给我任何类型的错误,这意味着打字稿允许将回调作为对象类型。

我如何确保这会导致错误?

小智 5

对象和函数本质上是同一件事,但是类型可以帮助我们消除它们功能之间的歧义。

考虑一下:

const foo1: { (): string } = () => "";
Run Code Online (Sandbox Code Playgroud)

该变量foo的类型为object,但该对象是可调用的...它是一个函数,并且确实可以被调用,但您不能去设置bar对其调用的属性。

foo1(); // This works
foo1.bar = 5; // This, not so much.
Run Code Online (Sandbox Code Playgroud)

还要考虑这一点:

const foo2: { bar?: number; } = {};
Run Code Online (Sandbox Code Playgroud)

该变量foo有一个被调用的属性bar。可以设置该属性,但无法调用该对象,因为它的类型不是可调用的。

foo2.bar = 5; // This works
foo2(); // This, not so much.
Run Code Online (Sandbox Code Playgroud)

那么,让我们看看您的原始输入:

sortQuery = {}
Run Code Online (Sandbox Code Playgroud)

sortQuery是一个对象,但这就是我们对它所知的全部。它没有任何属性,不可调用,它只是一个对象。

我们已经知道函数一个对象,因此您可以为它分配一个函数。但是,您将无法调用它,因为它没有定义为可调用。

const sortQuery: {} = () => ""; // This works.
sortQuery(); // This, not so much.
sortQuery.bar = 5; // Nor this.
Run Code Online (Sandbox Code Playgroud)

如果您可以完全控制源代码,那么解决此问题的一种方法是从多个参数转移到具有命名属性的单个参数:

const foo = (params: { collection: string, query: object, sortQuery: {}, cb?: Function }) => { };

foo({ collection: "", query: {}, sortQuery: {} }); // Fine
foo({ collection: "", query: {}, sortQuery: {}, cb: () => { } }); // Fine
foo({ collection: "", query: {}, cb: () => { } }); // Not Fine
Run Code Online (Sandbox Code Playgroud)

这通过要求名称而不是依赖于函数调用中的位置来消除任何歧义。

  • 而是使用破坏性参数 `fnName( { p1, p2, p3 } : { p1: string, p2: number, p3: boolean })` (2认同)

Get*_*awn 5

通过 TypeScript条件(TS v2.8),我们可以使用以下方法从类型中Exclude排除:FunctionsobjectExclude<T, Function>

let v = {
  find: <T extends object>(collection: string, query: object, sortQuery: Exclude<T, Function>, cb?: (a: string, b: string) => void) => {
  }
}

// Invalid
v.find('vendors', { type: 'repair' }, (a, b) => { })
v.find('vendors', { type: 'repair' }, 'I am a string', (a, b) => { })

// Valid
v.find('vendors', { type: 'repair' }, { dir: -1 })
v.find('vendors', { type: 'repair' }, { dir: -1 }, (a, b) => { })
Run Code Online (Sandbox Code Playgroud)

然后可以像这样设置默认参数值:

sortQuery: Exclude<T, Function> = <any>{}
Run Code Online (Sandbox Code Playgroud)

如下图所示,前两次调用会引发错误find,但后两次调用不会引发错误find

TypeScript 排除

然后显示的错误如下:

  • [ts] 类型“(a, b) => void”的参数不可分配给类型“never”的参数。[2345服]第2345服[双线] 新服
  • [ts]“我是字符串”类型的参数不可分配给“对象”类型的参数。[2345服]第2345服[双线] 新服