Missing interface declaration for _.split in Lodash.d.ts - javascript

I'm working on a typescript project that relies extensively on Lodash (using lodash.d.ts). I've implemented a method that uses the _.split function, but this does not seem to be implemented just yet. (Ref. the .ts file, where it's included in the 'Later' section).
Is there a way for me to work around this, so it doesn't stop my build? (Building from Visual Studio and sometimes as a Grunt task).
Here's the error:
TS2339 Property 'split' does not exist on type 'LoDashStatic'
As a contextual reference, here's the code:
private parseString(text: string) {
const arr = _.map(_.split(text, ","),
(x: string) => {
let index = x.indexOf(":");
return [
x.substring(0, index),
_.trim(x.substring(index + 1), "()")
];
});
console.log(_.fromPairs(arr));
return _.fromPairs(arr);
}
The code works, however it's just annoying that the build stops as a result of this.

Resolved this the 'easy' way, using native Javascript string.split() method.

Related

Use 'strictNullChecks' annotation on one function/method

I have this method:
remove(node: SortedQueueNode<V, K>) : SortedQueueNode<V, K>{
// ...
return node.parent;
}
there are multiple return statements in the function body, and I want to avoid null/undefined return statements. Is there an annotation I can add to just this method, something like:
// #ts-strictNullChecks
remove(node: SortedQueueNode<V, K>) : SortedQueueNode<V, K>{
// ...
return node.parent;
}
As of TypeScript 4.9 the --strictXXX compiler options are each either enabled or disabled; there is no support for applying them at a more granular or modular level.
But, at microsoft/TypeScript#49886 there is an implementation of per-file compiler options, where some compiler options including --strictNullChecks can be enabled or disabled for an individual file in a project. As of today this has not been merged into the main branch, but it is part of the TypeScript 5.0 iteration plan at microsoft/TypeScript#51362, so there's a good possibility that it will be released with TypeScript 5.0. Until and unless it is released, I think you're stuck.
If and when it is released, it wouldn't be exactly the same as a function-scoped compiler option. But you could get a similar effect to by refactoring your code so that the parts you want to check differently live in different files, such as:
// someFile.ts
// #ts-strictNullChecks true
// -------------------> ^^^^ change to false to see error go away
Foo.prototype.remove = function <V, K>(node: SortedQueueNode<V, K>) { // error!
//~~~~~~~~~~~~~~~~~~ <-- undefined is not assignable to SortedQueueNode<V, K>
return node.parent;
}
where the //#ts-strictNullChecks true compiler directive causes the whole file to be checked strictly for null/undefined-related errors, and where your remove() method is implemented in this file separately from the rest of the class.
You can see this in action now, by using the 4.9.0-pr-49886-38 version of TypeScript: Playground link to code, 4.9.0-pr-49886-38

Jest import of plain javascript results in unexpected token

Firstly: as far as I can tell, this is not a duplicate. The other questions with similar problems are all slightly different, e.g. use a transformation like babel or have problems with transitive imports. In my case I have no transformation, I have one test file and one file imported file that will be tested. I just started using jest and use the default setting, so there is no configuration file to post.
When I try to run my tests I get the error message:
Test suite failed to run
Jest encountered an unexpected token
This usually means that you are trying to import a file which Jest cannot parse, e.g. it's not plain JavaScript.
The tested file:
export function showTooltip(x, y, content) {
const infoElement = document.getElementById('info');
infoElement.style.left = `${x}px`;
infoElement.style.top = `${y}px`;
infoElement.style.display = 'block';
infoElement.innerText = createTooltipText(content);
}
function createTooltipText(object) {
return Object.keys(object)
.filter(key => key != 'id')
.map(key => `${key} : ${object[key]}`)
.join('\n');
}
export function hideTooltip() {
const infoElement = document.getElementById('info');
infoElement.style.display = 'none';
}
The test:
import {showTooltip, hideTooltip} from '../../../src/public/javascripts/tooltip.js';
const TOOLTIP_DUMMY = {
style: {
left: 0,
top: 0,
display: '',
innerText: ''
}
};
test('showTooltip accesses the element with the id \'info\'', () => {
const getElementByIdMock = jest.fn(() => TOOLTIP_DUMMY);
document.getElementById = getElementByIdMock;
showTooltip(0, 0, {});
expect(getElementByIdMock).toHaveBeenCalledWith('info');
});
test('hideTooltip accesses the element with the id \'info\'', () => {
const getElementByIdMock = jest.fn(() => TOOLTIP_DUMMY);
document.getElementById = getElementByIdMock;
hideTooltip();
expect(getElementByIdMock).toHaveBeenCalledWith('info');
});
As you can see I am using plain javascript so I am not sure what to do here. The error message gives further hints about Babel which does not really apply to my case.
Sidenote: My test might be flawed. I am currently trying to figure out how to use mocks to avoid interaction with the document and I am not sure if that is the way. However this is not the point of this question as it should not affect the ability of the tests to run, but I am very open for suggestions.
EDIT: Why this is not a duplicate to this question: It kind of is, but I feel that question and the accepted answer were not really helpful for me and hopefully someone will profit from this one.
I have found the solution to my problem:
As suggested in this answer, you need to use Babel. This can be done as suggested here, but I used #babel/env-preset as it is suggested on the Babel website.
This left me with the problem that jest internally uses babel-core#6.26.3, but at least babel 7 was required. This problem is described here. I used the temporary fix of manually copying and overwriting babel-core from my node-modules directory to the node-modules directories of jest-config and jest-runtime. This dirty fix is also described in the previous link.
I have yet to find a clean solution, but at least this works.
Use global.document.getElementById = getElementByIdMock; In some configurations Jest doesn't have access to document object directly.

Tern Fails to use JSDoc Type Information

I'm trying to use Tern to perform type inference on some Javascript code. However, the type inference doesn't seem to be using the JSDoc comments alongside the code.
I'm using the code /** #type {Foo} */ let x; as an example of the problem. On the Tern website's demo page (which uses CodeMirror), the editor is able to infer that the type of x is Foo.
Yet, when run locally via node, I get this back: { type: '?', exprName: 'x' }.
Here's a snippet that replicates the issue:
const tern = require('tern');
const ternServer = new tern.Server({
plugins: {
doc_comment: {
strong: true
}
}
});
const js = `/** #type {Foo} */ let x;`;
ternServer.addFile("main", js);
ternServer.request({
query: {
type: "type",
file: "main",
start: js.length - 2,
end: js.length - 2
}
}, console.log);
Tern has otherwise been working perfectly fine for type inference. It's when using the JSDoc comments that it doesn't seem to work with the way I've initialized and called it.
I even set the doc_comment plugin to strong, which means that JSDoc types are preferred over normally inferred types, but to no avail.
Any ideas how to get this to work?
As it turns out, you have to import the doc_comment plugin in order to use it. Otherwise, setting the plugins option for the tern server won't do anything.
Simply adding require("tern/plugin/doc_comment"); to the top of the file solved the problem.

Getting an arbitrary property from a JavaScript object in Dart

Edit: Here is a minimal project that illustrates my issue. You can see the error described by serving it to the browser: pub get and then either pub serve (dartium) or pub build --mode=debug (other browsers).
How can I access an arbitrary JavaScript property from Dart through a JsObjectImpl? I am using the ace.js library with an interop to Dart that I've adapted from a typescript interface, and the method I am calling returns a plain javascript object with key-value pairs.
Dart gives me a JsObjectImpl, which cannot be casted to a Map or a JsObject, both of which have [] accessors. It confusingly seems to inherit from the deprecated JSObject (note the 's' is capitalized in the latter) which does not have the [] accessor, so I can't get the data out.
Some error messages:
When attempting a cast from JsObjectImpl to JsObject:
ORIGINAL EXCEPTION: type 'JSObjectImpl' is not a subtype of type 'JsObject' of 'obj' where
JSObjectImpl is from dart:js
JsObject is from dart:js. I get a similar message when using Map as well.
Looking at the object in the debugger, I can frustratingly see the property in JS view but not in the Dart object: The 4: Object is the data I want.
Ok, this was a fun one, happy holidays :)
It looks like Map is not a supported auto-conversion for package:js. So a couple of things:
Filed https://github.com/dart-lang/sdk/issues/28194
Sent your a PR introducing a workaround
For interested parties, we can use the browser-native Object.keys:
#JS()
library example;
import 'package:js/js.dart';
/// A workaround to converting an object from JS to a Dart Map.
Map jsToMap(jsObject) {
return new Map.fromIterable(
_getKeysOfObject(jsObject),
value: (key) => getProperty(jsObject, key),
);
}
// Both of these interfaces exist to call `Object.keys` from Dart.
//
// But you don't use them directly. Just see `jsToMap`.
#JS('Object.keys')
external List<String> _getKeysOfObject(jsObject);
And call it once we have an arbitrary JavaScript object:
var properties = jsToMap(toy.getData());
print(properties);
I had to modify #matanlurey solution so it works on dart 2.12 and is recursive.
import 'dart:js';
/// A workaround to deep-converting an object from JS to a Dart Object.
Object jsToDart(jsObject) {
if (jsObject is JsArray || jsObject is Iterable) {
return jsObject.map(jsToDart).toList();
}
if (jsObject is JsObject) {
return Map.fromIterable(
getObjectKeys(jsObject),
value: (key) => jsToDart(jsObject[key]),
);
}
return jsObject;
}
List<String> getObjectKeys(JsObject object) => context['Object']
.callMethod('getOwnPropertyNames', [object])
.toList()
.cast<String>();

Working with KendoUI, TypeScript and DefinitelyTyped

I'm working with an old version of KendoUI (v2013.2.716) and TypeScript (v 0.9.5). I would update, but everything breaks and I'm on a tight deadline. Will do that later.
Anyway, I'm using the DefinitelyTyped kendo.d.ts and everything was fine until I tried this:
var grid = $('.k-grid').data('kendoGrid');
grid.dataSource.transport.options.read.url = newDataSource;
grid.dataSource.read();
This works fine, but Visual Studio doesn't like it. I get:
The property 'transport' does not exist on value of type 'kendo.data.DataSource'
I've had issues like this before and pretty sure I can make a custom.d.ts file to work around this error, but not sure how. Does anyone know how I can create a workaround?
You can 'extend' existing type interfaces by simply declaring them twice, they will be merged.
So, this:
interface A {
propB: string;
}
interface A {
propC: number;
}
Will be treated by the compiler as:
interface A {
propB: string;
propC: number;
}
In your case you can add a custom declaration file, and add the following:
module kendo {
interface data {
interface DataSource {
transport: any;
}
}
}
Of course, you can add typings for transport as well.
I have run into similar issues.
Most of the times, I fixed it by casting it to any.
Try this -
(<any>grid.dataSource.transport).options.read.url = newDataSource;
Or, you can try this too -
(<kendo.data.DataSource>.grid.dataSource).transport.options.read.url = newDataSource;
But, fist option should work for sure!
Hope, this helps
This is not a particularly elegant answer, but here's what I do on a tight deadline:
grid.dataSource['transport'].options.read.url = newDataSource;
To me this is generally not advised as the whole reason you're using typescript is to maintain type safety, however in the case of 3rd party libraries I do make exceptions here and there by casting to any or using indexer syntax as above.

Categories