Transitioning from Javascript to Typescript with the integration of Knockout and DevExtreme interfaces

Currently facing challenges with transitioning from Javascript to Typescript, especially when it comes to creating a devextreme control.

In the past, I would set up an object in my viewmodel for devextreme controls like this:

self.myButton = {
  text: 'Click Me',
  disabled: ko.purecomputed(function(){ return self.myobservable().length>0;});
}

This method worked effectively. In typescript, I attempted...

myButton: DevExpress.ui.dxButtonOptions;

Then within the constructor...

....
self.myButton = {
  text: 'Click Me',
  disabled: ko.purecomputed(function(){ return self.myobservable().length>0;});
}
...

This resulted in an error "Type KnockoutComputed is not assignable to type boolean", which makes sense. So, how should this be properly handled?

I could simply use:

myButton: any; 

in the button options declaration, but isn't that defeating the purpose of using Typescript?

While it may work, I initially moved to Typescript to also leverage tools like webpack, so it's not all bad. I just want to ensure I'm doing it correctly.

Appreciate your assistance.

Answer №1

After exploring options with knockout-decorators and knockout-es5, I have come to the conclusion that using any as the type for widget options would be the simplest solution for your issue.

The template for both approaches remains the same:

<div data-bind="dxButton: buttonOptions"></div>

Using knockout-es5

This method involves creating a computed property separately, which may lead to loss of some strong typing benefits due to lack of well-typed creation process.

// Import necessary modules from DevExtreme.
import "devextreme/ui/button";
import DevExpress from "devextreme/bundles/dx.all";

import * as ko from "knockout";
// Import knockout-es5 for track and defineProperty functionality
import "knockout-es5";

// Util function to enhance compute creation using keyof
const createComputed = <T>(prototype: T, key: keyof T, computedFunc: Function): void => {
    ko.defineProperty(prototype, key, computedFunc);
}

class DevextremeTestViewModel {
    clickCounter: number = 0;
    buttonOptions: DevExpress.ui.dxButtonOptions;

    constructor() {
        this.buttonOptions = {
            text: "Start",
            onClick: this.increaseCounter
        };

        // Make clickCounter observable
        ko.track(this, ["clickCounter"]);

        // Assign computed value to text property in widget options
        createComputed(this.buttonOptions, "text", () => {
            return `Clicked ${this.clickCounter} times`;
        });
    }

    increaseCounter(): void {
        this.clickCounter++;
    }
}

Using knockout-decorators

This approach also requires creating a computed property separately within the viewModel. It involves a "hack" to copy the getter of the computed property to the widget options:

import "devextreme/ui/button";
import DevExpress from "devextreme/bundles/dx.all";
// Include required decorators
import { observable, computed } from "knockout-decorators";

// Function to copy getter from one property to another
const copyGetter = <T, TProp>(prototype: T, key: keyof T, propProto: TProp, propertyKey: keyof TProp) => {
    let getter = Object.getOwnPropertyDescriptor(propProto, propertyKey).get;
    Object.defineProperty(prototype, key, {
        get: getter
    });
}

class DevextremeTestViewModel {
    // Create observable
    @observable clickCounter: number = 0;
    // Create computed based on observable
    @computed({ pure: true }) get buttonText(): string {
        return `Clicked ${this.clickCounter} times`;
    };
    buttonOptions: DevExpress.ui.dxButtonOptions;

    constructor() {
        this.buttonOptions = {
            text: this.buttonText,
            onClick: this.increaseCounter
        };

        // Copy getter from computed to options property
        copyGetter(this.buttonOptions, "text", this, "buttonText");
    }

    increaseCounter(): void {
        this.clickCounter++;
    }
}

Using any

Given the limitations in changing the interface of widget's options, utilizing any seems like the cleanest way forward:

import "devextreme/ui/button";
import DevExpress from "devextreme/bundles/dx.all";
import { observable, computed } from "knockout-decorators";
import * as ko from "knockout";

class DevextremeTestViewModel {
    // Create observable
    @observable clickCounter: number = 0;
    buttonOptions: any = {
        text: ko.pureComputed(()=> {
            return `Clicked ${this.clickCounter} times`;
        }),
        onClick: this.increaseCounter
    };

    increaseCounter(): void {
       this.clickCounter++;
    }
}

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

What steps should I follow to set up a TypeScript project that incorporates JavaScript modules compiled from PureScript?

TL;DR I am looking to develop TypeScript typings for compiled PureScript modules and include them in my npm package. I am willing to manually maintain these typings, but I am struggling with the configuration needed in tsconfig.json (up and downstream) and ...

Using a component again with variations in the input data

I'm looking to reuse a card component to display different types of data, but the @Input() properties are for different objects (articles and events). Parent HTML: <card-component [headline]="Articles" [content]="art ...

Angular 5 does not recognize the function submitEl.triggerEventHandler, resulting in an error

Greetings! I am currently working on writing unit test cases in Angular5 Jasmine related to submitting a form. Below is the structure of my form: <form *ngIf="formResetToggle" class="form-horizontal" name="scopesEditorForm" #f="ngForm" novalidate (ngSu ...

Tips for type guarding in TypeScript when using instanceof, which only works with classes

Looking for a way to type guard with TypeScript types without using instanceof: type Letter = 'A' | 'B'; const isLetter = (c: any): c is Letter => c instanceof Letter; // Error: 'Letter' only refers to a type, but is being ...

Utilizing a loaded variable containing data from an external API request within the useEffect() hook of a React component

Essentially, I have an API request within the useEffect() hook to fetch all "notebooks" before the page renders, allowing me to display them. useEffect(() => { getIdToken().then((idToken) => { const data = getAllNotebooks(idToken); ...

Tips for implementing type safety in a generic class to verify that the response type aligns with the anticipated type in TypeScript

In TypeScript, I have created a QueryFactory class with two generic type parameters: TQuery and TResponse. My goal is to ensure type safety so that if the TResponse type does not match the expected response type of the TQuery type, the compiler will throw ...

When utilizing TypeORM, if a OneToMany relationship is established with a ManyToOne relationship

In my database, I established a relationship between two tables: Users and Tasks. As per the Typeorm documentation. Here are the Models: @Entity('tasks') class Tasks { @PrimaryGeneratedColumn('uuid') id: string; @Column() nam ...

Looking to retrieve CloudWatch logs from multiple AWS accounts using Lambda and the AWS SDK

Seeking guidance on querying CloudWatch logs across accounts using lambda and AWS SDK Developing a lambda function in typescript Deploying lambda with CloudFormation, granting necessary roles for reading from two different AWS accounts Initial exe ...

The bespoke node package does not have an available export titled

No matter what I do, nothing seems to be effective. I have successfully developed and launched the following module: Index.ts : import ContentIOService from "./IOServices/ContentIOService"; export = { ContentIOService: ContentIOService, } ...

Is it possible to use string indexes with jQuery each method in Typescript?

When running a jQuery loop in Typescript, I encountered an issue where the index was being reported as a string. $.each(locations, (index, marker) => { if(this.options && this.options.bounds_marker_limit) { if(index <= (this.opt ...

The index signature for strings appears to be duplicated in this TypeScript file, causing an

I am attempting to create a type with an index signature in TypeScript. Here is the code snippet: export interface LoginState { account: { [userName: string]: string; [password: string]: string; }; } However, I ...

Angular 7 ESRI loader search widget focus out bug: finding a solution

I am currently working on implementing an ESRI map using esri-loader in my Angular application. Everything seems to be working fine, but I am encountering an error when typing something into the search widget and then focusing out of it. "Uncaught Typ ...

Array modification update

Struggling with adding an object to the objectListDrawArea array outside of the original class. The object is added successfully, but my *ngFor loop cannot find it. I'm stumped on how to resolve this issue - should I use Observable? If so, can you pro ...

What is the process for exporting a TypeScript function so that it can be accessed from the command line in a web browser?

const ident = (v) => {return v}; export default ident; Although the code compiles successfully, if I attempt to call ident from the browser's command line, it throws an error: VM1228:1 Uncaught ReferenceError: ident is not defined at <anon ...

Searching for evenly distributed GPS coordinates across an area

I have a collection of data points representing mountain peaks in the European Alps that I would like to showcase on a map. To prevent cluttering the user interface, I currently retrieve the highest peaks within the visible area of the map by sorting them ...

Image paths becoming unresponsive following package upgrades

My Angular2 application was originally built using angular-cli (v 1.0.0) and webpack2. Within a component, I had the ability to reference an image like so: <div class="country-flag"> <img [src]="src/assets/flags/32/jp.png" [width]="flagIconSiz ...

Mastering the art of debugging feathersjs with typescript on VS Code

I am facing an issue while trying to debug a TypeScript project with FeathersJS using VSCode. Whenever I try to launch the program, I encounter the following error: "Cannot start the program '[project_path]/src/index.ts' as the corresponding J ...

Exploring date comparisons in TypeScript and Angular 4

I'm currently working on a comparison of dates in typescript/angular 4. In my scenario, I've stored the system date in a variable called 'today' and the database date in a variable named 'dateToBeCheckOut'. My goal was to filt ...

Monitoring the current scroll position and updating other components on changes

Is there a way to easily monitor the scroll position of the browser and inform multiple components about it? For example, I would like to dynamically change the classes of different elements on the page as the user scrolls. In the past, with older version ...

Using Angular CLI with ES6 instead of TypeScript for your development needs can offer a

Is there a way to utilize an ES6 transpiler such as Babel instead of TypeScript in an Angular CLI project? Are there any specific flags for specifying the script language, similar to using --style? Thank you. ...