Converting Abstract Type Members from Scala to TypeScript: A Handy Snippet

I have a brief example of a value level and type level list in Scala

sealed trait RowSet {
  type Append[That <: RowSet] <: RowSet

  def with[That <: RowSet](that: That): Append[That]
}

object RowSet {

  case object Empty extends RowSet {
    type Append[That <: RowSet] = That

    override def with[That <: RowSet](that: That): Append[That] = that
  }

  case class Cons[A, B <: RowSet](head: A, tail: B) extends RowSet { self =>
    type Append[That <: RowSet] = Cons[A, tail.Append[That]]

    override def with[That <: RowSet](that: That): Append[That] = Cons(head, tail ++ that)
  }
}

Currently attempting to convert this to TypeScript. Since TypeScript lacks the Abstract Type Members feature, resolving without type casting seems challenging.

My current TypeScript implementation (also accessible on Playground)

abstract class RowSet {
    abstract with<That extends RowSet>(that: That): RowSet
}

type Append<This extends RowSet, That extends RowSet> =
    This extends Cons<infer A, infer B> ? Cons<A, Append<B, That>> : That;

class Empty extends RowSet {
    public with<That extends RowSet>(that: That): That {
        return that;
    }
}

class Cons<A, B extends RowSet> extends RowSet {
    constructor(public readonly head: A, public readonly tail: B) {
        super();
    }

    public with<That extends RowSet>(that: That): Cons<A, Append<B, That>> {
        return new Cons(this.head, this.tail.with(that) as Append<B, That>)
    }
}

const x = new Cons(5, new Empty)    // Cons<number, Empty>
const y = new Cons("hi", new Empty) // Cons<string, Empty>
const z = x.with(y)                 // Cons<number, Cons<string, Empty>> 

Exploring ways to avoid casting in this scenario:

return new Cons(this.head, this.tail.with(that) as Append<B, That>)

Although TypeScript recognizes the value as Append<B, That>, the casting is needed due to using the with method from abstract class RowSet, resulting in Cons<A, RowSet>.

Is there an alternative definition for RowSet in TypeScript that would allow TypeScript to infer everything correctly without manual intervention?

Answer №1

Oleg Pyzhcov's insightful comment proved to be the key in solving this issue without the need for manual type casting. The application of F-bounded polymorphism offered a solution that effectively addresses the problem at hand.

The implementation now functions seamlessly without requiring any type casting. Everything operates as intended.

abstract class RowSet<T extends RowSet<T>> {
    abstract with<That extends RowSet<That>>(that: That): Append<T, That>
}

type Append<This extends RowSet<This>, That extends RowSet<That>> =
    This extends Cons<infer A, infer B> ? Cons<A, Append<B, That>> : That;

class Empty extends RowSet<Empty> {
    public with<That extends RowSet<That>>(that: That): That {
        return that;
    }
}

class Cons<A, B extends RowSet<B>> extends RowSet<Cons<A,B>> {
    constructor(public readonly head: A, public readonly tail: B) {
        super();
    }

    public with<That extends RowSet<That>>(that: That): Cons<A, Append<B, That>> {
        return new Cons(this.head, this.tail.with(that))
    }
}

const x = new Cons(5, new Empty)    // Cons<number, Empty>
const y = new Cons("hi", new Empty) // Cons<string, Empty>
const z = x.with(y)                 // Cons<number, Cons<string, Empty>> 

Experience the working solution on Playground

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

Error encountered while attempting to resume activity: TypeError - the function `callResume` is not defined in NativeScript Angular 2

When the resume event occurs, I must invoke the method this.callResume(). However, upon calling the method, a runtime error is thrown: TypeError: this.callResume is not a function I am uncertain about how to call a method from within the resume method ...

The TypeScript error code TS2339 is indicating that the 'modal' property is not recognized on the type 'JQuery'

I'm currently utilizing Typescript with AngularJS and have encountered an issue with modals when using the typed definition of jQuery library. The specific error message I am receiving is: 'error TS2339: Property 'modal' does not exist ...

Extracting information from an Observable in Angular: A comprehensive guide

I am currently working on an Angular application that interacts with a server through RESTful requests, and receives a JSON stream response containing objects of a specific type. The interface for these objects is as follows: export interface Personal { ...

Having trouble getting a local npm installation to work from a specific file path?

After following the instructions from this helpful link to install an npm package through a file path, I encountered an error when attempting to use it: Cannot find module '<module_name>' or its corresponding type declaration Are there an ...

Encountering a syntax issue with pipeable operators in Angular Rxjs

I am currently in the process of rewriting this code snippet: Observable .merge(this.searchQuery$, this.lazyQuery$) .do(() => this.loadingPage()) .map(filter => this.buildURL("jaume", Config.security['appName'], filter)) .s ...

When using ngx-slider in Angular, it unexpectedly fires off when scrolling on mobile devices

I am currently developing a survey application that utilizes ngx-sliders. However, I have encountered an issue on mobile devices where users unintentionally trigger the slider while scrolling through rows of slider questions, resulting in unintended change ...

I'm encountering an issue with one of my routes not loading correctly in Angular 4 Universal

I have been working on implementing Universal and I believe I've made significant progress. My project is built on this seed. However, when I run "npm start", only the /about and /contact pages render successfully. The /home page does not render at al ...

Initially, when an iframe is loaded in Angular 10, it may display a 404 error page

Hey there! I'm currently using the HTML code below to incorporate an iframe and display an external page hosted on the same domain so no need to worry about cross domain issues: <iframe frameborder="0" [src]="url"></iframe ...

How can the map function be executed sequentially every second, using async functions, fetch API, and promises, without running in parallel?

I am facing an issue with my map function, as it needs to fetch data from an online API that only allows one request per second. I attempted to solve this problem by implementing the following code: const sleep = (ms: number) => { return new Promise(( ...

Application of Criteria for Zod Depending on Data Stored in Array Field

I am currently working on an Express route that requires validation of the request body using Zod. The challenge arises when I need to conditionally require certain fields based on the values in the "channels" field, which is an array of enums. While my cu ...

In TypeScript, fetch JSON data from a URL and gain access to an array of JSON objects

I'm struggling to find a solution and implement it in the correct format. An API returns a JSON file to me via a URL. The format is as follows: {"success":true, "data":[ { "loadTimestamp":"2022-07-20T ...

tsconfig.json: No input files were detected in the configuration file

I am encountering an issue with my tsconfig.ts file where it says "No inputs were found in config file 'd:/self-study/Web/Learning/Express/tsconfig.json'. Specified 'include' paths were '["**/*"]' and 'exclude&a ...

Converting a string into a Date in Typescript while disregarding the timezone

Upon receiving a date in string format like this (e.g.): "11/10/2015 10:00:00" It's important to note that this is in UTC time. However, when creating a Date object from this string, it defaults to local time: let time = "11/10/2015 10:00:00"; let ...

Exploring Angular: Embracing the Power of Query String Parameters

I've been struggling with subscribing to query string parameters in Angular 2+. Despite looking at various examples, I can't seem to make it work. For instance, on this Stack Overflow thread, the question is about obtaining query parameters from ...

When using TypeScript, default imports can only be done with the 'esModuleInterop' flag enabled

Below is my current code snippet in index.ts: import { User } from "./datatypes" import express from 'express'; console.log("hello world") Displayed is the content of my package.json: { "name": "simple_app& ...

What might be the reason behind Array concatenation causing an issue in TypeScript?

I am relatively new to TypeScript and recently encountered an issue while working with classes. class DataStorage { private data:string[] = []; addItem(item: string){ this.data.push(item); } removeItem(item: string){ ...

Retrieve the current step index in Angular Material Design Stepper

In my endeavors to retrieve the selected step within a component utilizing Angular Material Design stepper, I am encountering some issues. My current approach involves using the selectedIndex property, but it consistently returns "1" instead of the desire ...

When using Protractor with Typescript, you may encounter the error message "Failed: Cannot read property 'sendKeys' of undefined"

Having trouble creating Protractor JS spec files using TypeScript? Running into an error with the converted spec files? Error Message: Failed - calculator_1.calculator.prototype.getResult is not a function Check out the TypeScript files below: calculato ...

Generating Graphql types for React using graphql-codegen when Apollo Server is in production mode: A step-by-step guide

Everything functions perfectly when backend mode is set to NODE_ENV: development, but in production mode I encounter an error with graphql-codegen: Error on local web server: Apollo Server does not allow GraphQL introspection, but the query contains _sc ...

Testing MatDialog functions in Angular: Learning how to open and close dialogues

I am currently facing an issue with testing the MatDialog open and close functions. No matter what I try, I cannot seem to successfully test either the open or close functions. I am wondering how I can mock these functions in order to properly test them. W ...