Typescript Duplicate Function Implementation - javascript

I have defined the following two function signatures in the same Typescript class, i.e.,
public emit<T1>(event: string, arg1: T1): void {}
and
public emit<T1,T2>(event: string, arg1: T1, arg2: T2): void {}
However when transpiling the typescript I get the following error
error TS2393: Duplicate function implementation.
I thought you could overload functions in typescript providing the number of parameters in the function signature were different. Given that the above signatures have 2 and 3 parameters respectively, why am I getting this transpilation error?

I'm assuming your code looks like this:
public emit<T1>(event: string, arg1: T1): void {}
public emit<T1,T2>(event: string, arg1: T1, arg2: T2): void {}
public emit(event: string, ...args: any[]): void {
// actual implementation here
}
The problem is that you have {} after the first 2 lines. This actually defines an empty implementation of a function, i.e. something like:
function empty() {}
You only want to define a type for the function, not an implementation. So replace the empty blocks with just a semi-colon:
public emit<T1>(event: string, arg1: T1): void;
public emit<T1,T2>(event: string, arg1: T1, arg2: T2): void;
public emit(event: string, ...args: any[]): void {
// actual implementation here
}

export {}
Just add this line at the top of the typescript file

Because you open both Typescript and JavaScript file in the same window in IDE you can solve this problem with three ways:
First Solution:
Open typescript file only or JavaScript file only
Second solution - run this command:
tsc --init
This command will create tsconfig.json file for typescript configuration
Last Solution write
export{}
In the top of the file

Related

How do I override type of a standard library function in TypeScript?

I'm working on a polyfill that extends the behavior of the JSON.parse() function by adding an optional third argument to the reviver callback function.
The polyfill should be imported like this for the end users:
import 'my-polyfill';
The JSON.parse() is defined in the node_modules/typescript/lib/lib.es5.d.ts like this:
// lib.es5.d.ts
interface JSON {
// …
parse(
text: string,
reviver?: (this: any, key: string, value: any) => any
): any;
// …
}
How do I override this definition, so when the polyfill is installed/imported my definition is used instead? E.g.:
interface ContextType {
// …
}
interface JSON {
parse<Type = any>(
text: string,
reviver?: (
key: string,
value: any,
context?: ContextType
) => any
): Type;
}
You could create declarations file and name it, for example, as polyfill.d.ts. Then, you should use standard way of extending global types as follows:
declare global {
interface JSON {
// Here, you should write your own implementation of `parse` function.
parse(variable: string): void;
}
}
export {};
export is required by TypeScript and you can learn more about it in this answer and its comments.
Don't forget, that when you create a separate library, this file should exist in final build directory. So, when some user installs your package and imports it, file polyfill.d.ts would be automatically applied by TypeScript.
I am not sure, that you are able to make TypeScript transfer declarations file (.d.ts) with its own functionality. To do this, we should use some hack, described here. We should name file not polyfill.d.ts, but polyfill.ts and import it in library's index.ts file, so it would be imported by TypeScript and transferred to build directory.

Using optional arguments of overloaded TypeScript methods

I'm using a package written in TS in a JS app. I have a method with multiple overloads:
overloadedMethod(stringParameter: string): void;
overloadedMethod(stringParameter: string, otherParameter: any): void;
And I call it like this:
if (!myOtherParameter) {
overloadedMethod(myStringParameter)
} else {
overloadedMethod(myStringParameter, myOtherParameter)
}
I do not like this one bit. Have I approached the problem incorrectly, or is there some way I can include the otherParameter only if it is not undefined or null?

How to ignore import statement in Typescript

log.ts has the following code
import {LOG} from './log'
LOG.e("tag","error");
LOG.f("tag","error");
LOG.d("tag","error");
I want the IntelliSense support for the TS file but don't want the consequences of import on generated javascript.
I simply want the following in my Javascript file (log.js)
LOG.e("tag","error");
LOG.f("tag","error");
LOG.d("tag","error");
log.ts file must contain only declarations.
declare class LOG {
public static e(a: string, b: string): void;
public static f(a: string, b: string): void;
public static d(a: string, b: string): void;
}
If you want to use an imported class, and it should not be included in the generated javascript, then you need to declare it using declare.
This class is not functional javascript code, but only declares the type of the class and its methods. When generating javascript, this class will not be included in the build.
But to execute such javascript, LOG must already be declared in the global environment.
See playground...

GWT JsInterop migration

I am trying to migrate from using compiler flag -XjsInteropMode JS but I run into a problem.
I have an interface called Module that looks like this:
#JsType
public interface Module {
#JsProperty
String getBasename();
}
Then I have another interface called AuthenticationModule which looks like this:
#JsType
public interface AuthenticationModule extends Module {
static final String MODULE_NAME = "authentication";
void logIn(String username, String password, JsConsumer<JavaScriptObject> onSuccess, JsConsumer<JavaScriptObject> onError);
void logOut(JsConsumer<JavaScriptObject> onSuccess, JsConsumer<JavaScriptObject> onError);
}
The module interface is just a marker interface, so when I was loading some module, I was able to cast it in the end to a module I wanted, example here:
#Override
public void getAuthenticationModule(
OnModuleLoaded<AuthenticationModule> onModuleLoaded) {
initializeIfNecessary();
JsArrayString requiredModules = JavaScriptObject.createArray().cast();
requiredModules.push(AuthenticationModule.MODULE_NAME);
modules.require(requiredModules, loadedModule -> {
onModuleLoaded.moduleLoaded((AuthenticationModule) loadedModule); // this line (the casting) throws ClassCastException
});
}
modules in this code is another interface which looks like this:
#JsType
public interface Modules {
#JsFunction
#FunctionalInterface
interface CallbackRequire {
void apply(Module module);
}
#JsProperty
String getBase();
#JsProperty
void setBase(String base);
void require(JsArrayString modules, CallbackRequire onload);
}
I followed the rules on how to migrate in this document:
https://docs.google.com/document/d/10fmlEYIHcyead_4R1S5wKGs1t2I7Fnp_PaNaa7XTEk0/edit#
I was not able to solve this issue. The best I could get was changing #JsType to #JsType(isNative = true). Then the casting was working, but another error occured, can't remember now, but I'm not sure if the isNative is really the right way to solve this issue.
After adding "(isNative = true)" to most of the interfaces, it all works again. The only thing I had to change were the MODULE_NAME fields, since they cannot be initialized in a native JsType.

Trouble getting typings to work in Typescript

Edit: I rewrote the post to better explain the steps I've taken and the issues I'm facing
I started out with a simple html/js page.
I'm also using xregexp from a cdn.
var reg = XRegExp("^lights:(?<option>on|off)$", "xgi");
var match = XRegExp.exec("lights:on", reg);
document.body.innerText = "Lights are: " + match["option"];
<!DOCTYPE html>
<html lang="en">
<head></head>
<body></body>
<script src="https://cdnjs.cloudflare.com/ajax/libs/xregexp/3.1.1/xregexp-all.js"></script>
<script src="app.js"></script>
</html>
Next, I want to convert the js file to typescript. So I:
rename .js to .ts
added declare var XRegExp:any; to the top of the ts file (described here)
ran tsc --init to create tsconfig file
ran tsc
So far, Typescript compiles fine, and page is still working.
Next, I want to replace the declare var XRegExp:any with an interface, one from a community-maintained declaration file.
I install the typings for xregexp via typings install dt~xregexp -SG (d.ts file link)
But I'm unable to access the interfaces declared in the .d.ts file, because the declaration uses declare module 'xregexp' and export =.
According to Typescript's documentation on Modules, export = is used with import = require(). But that involves module loading, which is not what I want.
I've tried Pelle's suggestion, but still have no idea how to declare an ambient variable using it.
declare var XRegExp:xregexp.OuterXRegExp; // <-- TS error: Module 'xregexp' has no exported member 'OuterXRegExp'
I would like to know:
The d.ts declaration uses declare module "xregexp". Can I refer to any of its inner interfaces/types without an import?
is it possible for me to create an additional d.ts to declare the ambient variable? (like what dt~react-global does) How do I do this, without modifying the downloaded d.ts ?
reason: I would rather not change the declaration file pulled via typings in case of future changes.
At this point, I'm not doing Node development, nor do I want to set up a module loader and/or browserify/webpack.
I simply want to convert an already-working js file to ts, and would like to make Typescript aware of an ambient variable XRegExp, which has an interface as specified in the downloaded d.ts. Is it possible for me to achieve this without too much ceremony?
[Edit]
So, I rewrote the declaration file, using jquery.d.ts as a guide.
This appears to work when loading via script or import, but doesn't answer my original question.
interface TokenOpts {
scope ? : string;
trigger ? : () => boolean;
customFlags ? : string;
}
interface XRegExp {
(pattern: string, flags ? : string): RegExp;
(pattern: RegExp): RegExp;
addToken(regex: RegExp, handler: (matchArr: RegExpExecArray, scope: string) => string, options ? : TokenOpts): void;
build(pattern: string, subs: string[], flags ? : string): RegExp;
cache(pattern: string, flags ? : string): RegExp;
escape(str: string): string;
exec(str: string, regex: RegExp, pos ? : number, sticky ? : boolean): RegExpExecArray;
forEach(str: string, regex: RegExp, callback: (matchArr: RegExpExecArray, index: number, input: string, regexp: RegExp) => void): any;
globalize(regex: RegExp): RegExp;
install(options: string): void;
install(options: Object): void;
isInstalled(feature: string): boolean;
isRegExp(value: any): boolean;
match(str: string, regex: RegExp, scope: string): any;
match(str: string, regex: RegExp, scope: "one"): string;
match(str: string, regex: RegExp, scope: "all"): string[];
match(str: string, regex: RegExp): string[];
matchChain(str: string, chain: RegExp[]): string[];
matchChain(str: string, chain: {
regex: RegExp;
backref: string
}[]): string[];
matchChain(str: string, chain: {
regex: RegExp;
backref: number
}[]): string[];
matchRecursive(str: string, left: string, right: string, flags ? : string, options ? : Object): string[];
replace(str: string, search: string, replacement: string, scope ? : string): string;
replace(str: string, search: string, replacement: Function, scope ? : string): string;
replace(str: string, search: RegExp, replacement: string, scope ? : string): string;
replace(str: string, search: RegExp, replacement: Function, scope ? : string): string;
replaceEach(str: string, replacements: Array < RegExp | string > []): string;
split(str: string, separator: string, limit ? : number): string[];
split(str: string, separator: RegExp, limit ? : number): string[];
test(str: string, regex: RegExp, pos ? : number, sticky ? : boolean): boolean;
uninstall(options: Object): void;
uninstall(options: string): void;
union(patterns: string[], flags ? : string): RegExp;
version: string;
}
declare module "xregexp" {
export = XRegExp;
}
declare var XRegExp: XRegExp;
This is not really a typescript problem: if you want to use npm packages, it is strongly recommended to use a dependency manager like browserify or webpack to them. (I would recommend browserify, if you're only looking for something to handle your modules).
If you really don't want to use the amazing power of npm, there is also a hacky way of solving this specific problem: changing the declaration file to declare a namespace instead of a module. Now you can effectively use the types anywhere in your project.
Just make these two changes to your declaration file, and you should be good to go:
declare a namespace instead of a module:
declare module 'xregexp' {
//becomes
declare namespace xregexp {
get rid of the export on the bottom of the page:
// remove this:
export = OuterXRegExp;
Now you can call anywhere in your project. Eg:
var outerxregexp: xregexp.OuterXRegExp.TokenOpts = { trigger: () => true }
But again, it is strongly recommended to pull in browserify or so 🙂
Try adding the following line on top of the file where you are using XRegExp:
/// <reference path="./path/to/downloaded/xregexp.d.ts" />
More on the matter triple slash directives
if you have a tsconfig.json you'll need to add the d.ts file like this:
{
"files": [
"path/to/definition/xregexp.d.ts",
]
}

Categories