I know that all javascript is valid typescript but I'd like to start converting my javascript to typescript conventions. I'm battling this one snippet of JS:
My standard Javascript that works
if (MyCompany === undefined) {
var MyCompany = {};
}
MyCompany.Uploader = MyCompany.Uploader || {};
MyCompany.Uploader.Core = function (config) {
'use strict';
function build() {
console.log("building");
}
return {
build: build
};
};
var config = {this: "that};
MyCompany.Uploader.Core(config).build(); // outputs building to console
I've been messing with multiple approaches and I feel like I not close enough.
My failed attempt at converting to Typescript
namespace MyCompany.Uploader {
export var Core = (config:any) => {
function build() {
console.log("building");
}
};
}
let configobj = {here:"there"};
MyCompany.Uploader.Core(configobj).build();
This simply doesn't work. I can't seem to access the build function. I'm sure this is a rookie mistake.
The error I get: Property build does not exist on type void
That's because you did not add an important part of your javascript code into the typescript version, and that's the return object which contains the reference for the build function, it should be:
namespace MyCompany.Uploader {
export var Core = (config: any) {
function build() {
console.log("building");
}
return {
build: build
}
};
}
let configobj = { here: "there" };
MyCompany.Uploader.Core(configobj).build();
You can also define interfaces for the config and the return object:
namespace MyCompany.Uploader {
export interface Config {
here: string;
}
export interface Builder {
build: () => void;
}
export var Core = (config: Config): Builder => {
function build() {
console.log(config.here);
}
return {
build: build
}
};
}
let configobj = { here: "there" };
MyCompany.Uploader.Core(configobj).build();
Related
I am trying to use lit-translate to translate my "Elmish" typescript website into different languages. I use webpack and dotnet.
Inside my index.ts I register the translate config:
registerTranslateConfig({
lookup: (key, config) => config.strings != null ? config.strings[key] : key,
empty: key => key,
loader: lang => {
return new Promise((resolve, reject) => {
resolve({"title": "Der Titel"});
})}
});
use("en");
(The loader is hardcoded because getting the localization file also didn't work, but that's not important for now).
Inside html I use get("title")or translate("title") to get the translation.
Instead of the translation, I either read [title] or
(part) => { partCache.set(part, cb); updatePart(part, cb); }
If I assign the result of translate() to a variable, I get the following result inside the Object:
TypeError: 'caller', 'callee', and 'arguments' properties may not be accessed on strict mode functions or the arguments objects for calls to them
at Function.r (<anonymous>:1:83)
at Module../src/index.ts (http://localhost:8080/app.bundle.js:9800:10)
at __webpack_require__ (http://localhost:8080/app.bundle.js:12476:33)
at http://localhost:8080/app.bundle.js:13494:11
at http://localhost:8080/app.bundle.js:13497:12
I already tried disabling webpack strict mode.
The full class looks like the following:
export class App extends Application<Model, Message, {}> {
#property({type: Boolean})
hasLoadedStrings = false;
init(): [Model, Cmd] {
const initModel: Model = {
openPage: [{title: "Home"}, home]
}
return [initModel, Cmd.none]
}
constructor() {
super();
this.hasLoadedStrings = false;
}
shouldUpdate (changedProperties: Map<string | number | symbol, unknown>) {
return this.hasLoadedStrings && super.shouldUpdate(changedProperties);
}
async connectedCallback () {
super.connectedCallback();
await use("en");
this.hasLoadedStrings = true;
}
}
customElements.define('my-element', App);
I solved the problem by writing my own little translation library.
The lit-translate package contains errors if you use it as suggested in the documentation so feel free to use my solution:
translate.ts:
const de_config =
{
"Category": {
"Home": "Start",
},
"Home": {
"Welcome back,": "Willkommen zurück,"
};
export function translate(key: string) {
const keyParts = key.split(".");
const keyCategory = keyParts.shift();
const keyWord = keyParts.join('.');
let translationIndexes = typedKeys(de_config);
for(let translationIndex of translationIndexes)
{
if(translationIndex == keyCategory) {
let translationKeys = typedKeys(de_config[translationIndex]);
for(let translationKey of translationKeys) {
if(translationKey == keyWord) {
return de_config[translationIndex][translationKey];
}
}
}
}
return key;
}
function typedKeys<T>(o: T): (keyof T)[] {
return Object.keys(o) as (keyof T)[];
}
Access translations with:
import { translate } from './translate';
translate("Category.Home");
One could also store translation object in a different file, write a function to change language dynamically, etc...
I've been stuck on this issue for a while. I cannot describe it accurately enough to find solutions online - apologies if it is a duplicate question.
I want to access helloWorld() from module.js:
export function HelperProvider() {
return class Helper {
constructor() {
}
helloWorld() {
console.log('Hello World');
}
}
}
In another file:
import { HelperProvider } from 'module.js'
const helperProvider = HelperProvider;
const helper = new helperProvider();
helper.helloWorld();
However, I encounter the following error:
Uncaught TypeError: helper.helloWorld is not a function
Any help would be very much appreciated.
You need to invoke the function HelperProvider to get the class.
const helperProvider = HelperProvider();
function HelperProvider() {
return class Helper {
constructor() {
}
helloWorld() {
console.log('Hello World');
}
}
}
const helperProvider = HelperProvider();
const helper = new helperProvider();
helper.helloWorld();
You are using module features that's not out of the box in nodejs, if you want to use modules you'll need to set type: "module" in the package.json file... see details
If you wanna use node ways:
module.js
function HelperProvider() {
return class Helper {
constructor() {}
helloWorld() {
console.log("Hello World");
}
};
}
module.exports = HelperProvider;
index.js
const HelperProvider = require("./Helper");
const helperProvider = HelperProvider();
const helper = new helperProvider();
helper.helloWorld();
I have the following fn that creates a proxy:
const getStorageProxy = (store?: Storage) => {
const proxyObj: { store?: DataStorage } = {};
return new Proxy(proxyObj, {
get(obj, prop) {
if (!obj.store) {
obj.store = new DataStorage(store);
}
return obj.store[prop];
},
});
};
And then I export several instances of this proxy:
const storageA = getStorageProxy(storeA);
const storageB = getStorageProxy(storeB);
export { storageA, storageB };
The reason for this is that I don't want to actually instantiate the DataStorage class unless it's used, and for convenience I want to be able to type:
import { storageA } from 'storage';
This all works. The problem is that according to TS both of my exported storage instances are of type DataStorage | void because of:
const proxyObj: { store?: DataStorage } = {};
They're actually not because of the getter in the proxy, but how do I tell this to TS without actually assigning something to proxyObj.store on instantiation?
How to dynamically extend class with method in TypeScript? Any examples?
I try:
UsersBlocksMyOrders.prototype.myFunc = function():any{
alert('23434');
};
But compiler give me a error.
Most often, you need to do something like:
interface UsersBlocksMyOrders {
myFunc(): any;
}
Otherwise the compiler doesn't know about it.
It even works with existing classes. For example:
interface String {
logit(): void;
}
String.prototype.logit = function () {
console.log(this);
}
let a = "string";
a.logit();
(code in playground)
Because you want to change something in a different module, which is called Module Augmentation, you need to do something like:
Import { UsersBlocksMyOrders } from "../pages/users/blocks/myorders";
declare module "../pages/users/blocks/myorders" {
interface UsersBlocksMyOrders {
logit(): void;
}
}
UsersBlocksMyOrders.prototype.logit = function () { console.log(this); }
Whenever possible (which it seems to be for you), edit the source code directly. Doing it like this should only be done on an as-needed basis.
This may help (from here):
function extend<T, U>(first: T, second: U): T & U {
let result = <T & U>{};
for (let id in first) {
(<any>result)[id] = (<any>first)[id];
}
for (let id in second) {
if (!result.hasOwnProperty(id)) {
(<any>result)[id] = (<any>second)[id];
}
}
return result;
}
class Person {
constructor(public name: string) { }
}
interface Loggable {
log(): void;
}
class ConsoleLogger implements Loggable {
log() {
// ...
}
}
var jim = extend(new Person("Jim"), new ConsoleLogger());
var n = jim.name;
jim.log();
I have a problem with type script generated js file and I can't solve it, this is my service file that I have problem with it:
namespace Providers {
export interface IProvider {
loadTranslations(translations: ITranslations, locale: string);
}
module.provider('lego', LegoProvider);
class LegoProvider implements ng.IServiceProvider ,IProvider{
loadTranslations(translations:ITranslations , locale: string) {
......
}
$get() {
.....
}
}
}
and generated js file is:
var Providers;
(function (Providers) {
module.provider('lego', LegoProvider); // this is the problem
var LegoProvider = (function () {
function LegoProvider() {
}
LegoProvider.prototype.loadTranslations = function (translations, locale) {
};
LegoProvider.prototype.$get = function () {
};
return LegoProvider;
}());
})(Providers || (Providers = {}));
It throws error because of the LegoProvider is variable and in that line it is still undefined. when I change code with this, it works correctly:
namespace Providers {
export interface IProvider {
loadTranslations(translations: ITranslations, locale: string);
}
class LegoProvider implements ng.IServiceProvider ,IProvider{
loadTranslations(translations:ITranslations , locale: string) {
......
}
$get() {
.....
}
}
module.provider('lego', LegoProvider); // I've moved this line to bottom
}
In typescript code LegoProvider class is accessible from both positions and it doesn't make sense for me that the first position doesn't work
It looks like a classic hoisting issue:
The generated js file has a provider that is in fact a constructor function that is stored in a variable (an IIFE to be percise):
var LegoProvider = (function () {
function LegoProvider() { ...
When you register the provider to your app, the variable hasn't been initialized yet, only declared. This is what actually happens:
var LegoProvider = undefined; // hoisting
module.provider('lego', LegoProvider);
LegoProvider = (function () {
function LegoProvider() { ...
Your correction has moved the registration after the full initialization of your LegoProvider class.