最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Typescript FunctionObject parameters - Stack Overflow

programmeradmin0浏览0评论

Why is typescript ES6 not detecting that objects are not functions?

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

Based off this function, you would assume that this would fail:

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

Since there is no sortQuery object but instead a callback function. This is not giving me any type of error and means that typescript is allowing the callback as the object type.

How do I ensure this results in an error?

Why is typescript ES6 not detecting that objects are not functions?

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

Based off this function, you would assume that this would fail:

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

Since there is no sortQuery object but instead a callback function. This is not giving me any type of error and means that typescript is allowing the callback as the object type.

How do I ensure this results in an error?

Share Improve this question asked Nov 27, 2018 at 16:33 user1779362user1779362 1,0101 gold badge13 silver badges29 bronze badges 6
  • Does it actually infer the sortQuery as an object type? or is it any? – Jonas Wilms Commented Nov 27, 2018 at 16:37
  • The same happens here, probably because functions are objects in JavaScript: typescriptlang.org/play/… – Frank Modica Commented Nov 27, 2018 at 16:38
  • Same results with this: find: (collection: string, query: object, sortQuery: object, cb?: Function) => { socketManager.call('find', collection, query, sortQuery, cb); } – user1779362 Commented Nov 27, 2018 at 16:39
  • Any way to ensure 'Function' type and not an object? – user1779362 Commented Nov 27, 2018 at 16:40
  • 2 Do you know the parameters and return types of the sortQuery? You could define it more specifically – Frank Modica Commented Nov 27, 2018 at 16:44
 |  Show 1 more comment

2 Answers 2

Reset to default 5

With TypeScript Conditionals (TS v2.8), we can use Exclude to exclude Functions from the object type using Exclude<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) => { })

A default parameter value can then be set like this:

sortQuery: Exclude<T, Function> = <any>{}

As you can see in the image below, errors are thrown for the first two calls to find, but not the second two calls to find:

The errors that then display are as follows:

  • [ts] Argument of type '(a, b) => void' is not assignable to parameter of type 'never'. [2345]
  • [ts] Argument of type '"I am a string"' is not assignable to parameter of type 'object'. [2345]

Objects and functions are fundamentally the same thing, but typings help us disambiguate between their functionality.

Consider this:

const foo1: { (): string } = () => "";

The variable foo has a type of object, but that object is callable ... it's a function, and it can indeed be called, but you can't go setting a property called bar on it.

foo1(); // This works
foo1.bar = 5; // This, not so much.

Also consider this:

const foo2: { bar?: number; } = {};

The variable foo has a property called bar on it. That property can be set, but the object can't be called, as it's not typed as callable.

foo2.bar = 5; // This works
foo2(); // This, not so much.

So, lets have a look at your original typing:

sortQuery = {}

sortQuery is an object, but that's all that we know about it. It doesn't have any properties, it's not callable, it's just an object.

We've already seen that a function is an object, so you can assign a function to it just fine. But, you won't be able to call it, as it's not defined as callable.

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

If you have full control of the source code, then one way to solve this is to move from multiple parameters, to a single parameter with named properties:

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

This removes any ambiguity by requiring names, rather than relying on position in the function call.

发布评论

评论列表(0)

  1. 暂无评论