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
Related
How can I tell TypeScript to use different-written methods (on the same class), depending on the used target option within the tsconfig.json file?
I'm currently re-writing one of my scripts into TypeScript to just "manage" one source, because at the moment I'm working with an ES5 and an ES6 file next to each other. Since TypeScript supports to just change the output target in the tsconfig.json file, I would just need one version to update and maintain.
The problem is I'm using a generator in the ES6 version, which theoretically shouldn't be this issue because TypeScript "adds" a pseudo generator to the top of my ES5 file. BUT: My Pseudo-Generator code, which I'm currently using on the ES5 file, is "cleaner" and way less code.
The Question
Is it possible to overwrite the respective "generator" method or using any special comment annotation (such as //#ts-target) to tell the compiler which code (function body) should be used depending on the used target in the configuration file? (Even If I couldn't found such solution on the official documentation).
An additional function or script, which can be added into the TypeScript compiler process would also help, I guess, because I'm compiling them using a small node.js script (which compiles both ES files without changing the tsconfig.json file directly.)
Or is there any kind of extension, to move different methods of the same class into different files? This way I could "extract" the respective "generator" methods, but this would cause another question: How can I link them depending on the target, because I'm using /// <reference /> linking on the main script file to get all together.
Any other idea?
class Option {
///#ts-target ES5
__walkerVariable1:any undefined
__walkerVariable2:any undefined
__walkerVariable3:any undefined
walker() {
/* Some Pseudo-Walker Code for ES5 */
}
///#ts-target ES6
*walker() {
/* Real Walker Code for ES6 */
}
}
Currently, a Pseudo-Generator code gets added in the ES5 version of my script. I want to prevent this by using a different method / function body with my own Pseudo-Generator. Therefore, I need to tell TypeScript that he should "ignore" the Pseudo-Generator in ES6 / the Real-Generator in ES5 and just render one of them depending on the used target option.
That was a bit tricky, but I found a solution thanks to this GitHub issue.
As mentioned above, I'm using my own node.js script to compile the TypeScript files into two different JavaScript versions (ES5 and ES6). Therefore, I'm using the TypeScript API with the ts.createProgram method. This method allows to add a host object as third parameter, which takes over some compiler processes and one of those is the file loader, called getSourceFile.
The rest is then relatively easy: Searching for a custom comment annotation (in my case \\\#ts-target:ES5 and \\\#ts-target:ES6 respectively) and filter them using RegExp. Maybe not the best solution, but it works!
function compileTS(){
let config = readConfig("ts/tsconfig.json");
let host = ts.createCompilerHost(config.options);
let sourceFile = host.getSourceFile;
// ES5 JavaScript
(function(config){
host.getSourceFile = function(filename) {
if(filename === "ts/options.ts"){
let file = fs.readFileSync("./ts/options.ts").toString();
file = file.replace(/[ ]+\/\/\/\#ts\-target\:ES6\s+([\s\S]*)\/\/\/\#ts\-target\:ES6/gm, "");
return ts.createSourceFile(filename, file, ts.ScriptTarget.ES5, true);
}
return sourceFile.call(host, filename);
}
let program = ts.createProgram(config.fileNames, config.options, host);
let emitResult = program.emit();
report(ts.getPreEmitDiagnostics(program).concat(emitResult.diagnostics));
if(emitResult.emitSkipped){
process.exit(1);
}
}(config));
// ES6 JavaScript
config.options.target = 2;
config.options.outFile = "../dist/js/tail.select-es6.js";
(function(config){
host.getSourceFile = function(filename) {
if(filename === "ts/options.ts"){
let file = fs.readFileSync("./ts/options.ts").toString();
file = file.replace(/[ ]+\/\/\/\#ts\-target\:ES5\s+([\s\S]*)\/\/\/\#ts\-target\:ES5/gm, "");
return ts.createSourceFile(filename, file, ts.ScriptTarget.ES2015, true);
}
return sourceFile.call(host, filename);
}
let program = ts.createProgram(config.fileNames, config.options, host);
let emitResult = program.emit();
report(ts.getPreEmitDiagnostics(program).concat(emitResult.diagnostics));
if(emitResult.emitSkipped){
process.exit(1);
}
}(config));
}
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.
I'm using the CodeMirror library which is awesome. The code editor that I'm istantiating is a part of a form and therefore I want to do a basic check with linting to see whether the user's input seems valid. Unless the code is fine, I don't want to process the form.
So the question is: is there a method on the CodeMirror editor instance that would allow me to retrieve the result of linting? I'm looking through the docs and Google but failed to find anything helpful. There's this performLint method that is added to the editor, however it does not return the results of linting.
There isn't a specific method to get the linting results, but there is a hook provided when you define a getAnnotations property in the lint options object.
Here's a basic options object that would trigger linting:
var codeMirrorOptions = {
"lineNumbers": true,
"indentWithTabs": true,
"mode": "css",
"gutters": ['CodeMirror-lint-markers'],
"lint": true
}
You can specify an object (instead of a boolean) as the lint property:
"lint": {
"getAnnotations": css_validator,
"async": true
}
Then, define your own validator function. This function can just call CodeMirror's bundled validator:
function css_validator(cm, updateLinting, options) {
// call the built in css linter from addon/lint/css-lint.js
var errors = CodeMirror.lint.css(cm);
updateLinting(errors);
}
At this point you've replicated the behavior of lint:true -- but now the errors variable contains an array of lint errors. If errors.length == 0, no errors were found.
Note: this sample assumes you are linting CSS, but the same would apply for other types.
The updateLinting function in lint.js passes its annotations (and editor) to the onUpdateLinting option:
if (options.onUpdateLinting) options.onUpdateLinting(annotationsNotSorted, annotations, cm);
so all you have to do is have your handler as the lint option property:
lint: { onUpdateLinting: handleErrors }
This is a hint for Dezzas Answer:
Write,
var errors = CodeMirror.lint.css(cm, options);
Otherwise, it will fail.
#Clem's answer led me in the right direction, though I did run into a few issues. The first was seeing Bad option: onUpdateLinting repeatedly in the console (See this reported Codemirror issue). The second was seeing the annotations array containing null entries sometimes. Here is my linting configuration passed into Codemirror that solves these issues. Note that I'm using react-codemirror2, but the options get passed into Codemirror in the same format. My component has an optional onLintingComplete callback that can be provided by the consuming component and you'll see that callback referenced below where it is passed the array of lint annotations:
lint: onLintingComplete
? {
onUpdateLinting: (_annotationsNotSorted, annotations) =>
onLintingComplete(
// sometimes codemirror includes null annotations in the array, so we want to filter these out
annotations.filter(annotation => annotation != null)
),
// This empty lint options object is needed here, see: https://github.com/codemirror/CodeMirror/issues/4198
options: {},
}
: true,
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.
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.