cancel
Showing results for 
Search instead for 
Did you mean: 
Reply
Dlabar
MVP

Splitting Up A Large TypeScript Class

Let's say I've extended the FormContext by creating a new TS class that wraps it, and adds helper functions.  This new ExtendedContext has functions like getDisplayValue(attName) which gets the attribute, handles it not being on the form, determines the attribute type, and appropriately returns what the "displayed value" is.  As I add more helper functions, the class get's bigger and bigger, which means I need to start splitting the class into more files, but I don't want the API to change. The consuming code should not have to know that it needs to create a DisplayExtendedContext class to call getDisplayValue, all functions should exist on the main extended context. What's the recommended approach?

 

My current approach feels wrong and looks like this:

 

// extendedContex.ts
import { DisplayContext } from "../context/display";

export class ExtendedContext implements XrmExt.ExtendedContext {
  public context: Xrm.FormContext // Actual Xrm.Client form context
  private display: DisplayContext;
  
  constructor(context: Xrm.FormContext){
	this.context = context;
	this.display = new DisplayContext(this);
  }
  
  public getDisplayValue(att: string): string {
	return display.getDisplayValue(att);
  }
}

// xrmExt.d.ts
declare namespace XrmExt {
	interface ExtendedContext {
		getDisplayValue(att: string): string;
	}
}

// ../context/display.ts
export class DisplayContext {
	private context: XrmExt.ExtendedContext;
	
	constructor(context: XrmExt.ExtendedContext){
		this.context = context;
	}
	
	public getDisplayValue(att: string): string {
		// Do logic here, with full access to the ExtendedContext
	}
}

 Here are the issues it has:

  1. I have to duplicate the pass throughs for ExtendedContext functions. So every function I add, I have to implement it in the smaller context class, and then add it as a pass through in the ExtendedContext class and to the ExtendedContext interface.  I'm lazy, I don't want to have to do that for every function.
  2. This one is more minor, but the ExtendedContext that is passed to the DisplayContext is not fully initialized, which could lead to null ref errors.  For example, if DisplayContext were to call a function on the XrmExt.ExtendedContext interface in it's constructor that it itself implements, the class level "display" field of the ExtendedContext class will not be populated, and a null ref exception would be thrown.  An "unspoken" rule of never access the ExendedContext from the constructor of one of the smaller classes would prevent this from ever being an issue.

I'm guessing Mixings might be the way forward here, but I'm just not sure.  Thoughts / Suggestions?

1 REPLY 1
cchannon
Impactful Individual
Impactful Individual

OK, this is a complex one, for sure, but I am not sure it is as complex as you think.

 

Inheritance between classes in TS does not intrinsically mean that you must redeclare every member at every level. Maybe I am missing something you're trying to do here, but why do you think you need to redeclare every member? Try the below super-simple example in a raw TS file: 

export class myclass{
    public aString: string;
    public aMethod: (() => void)
}

export class myOtherClass extends myclass{
    public aNumber: number;
    constructor(aNumber: number, aString: string) {
        super();
        this.aNumber = aNumber;
        this.aString = aString;
    }
}

let thing : myOtherClass = new myOtherClass(1,"thing");
thing.aMethod();

You'll see that even though myClass implements aMethod and myOtherClass does not, you can still invoke it on the class object instantiated as myOtherClass because myOtherClass extends myClass. And, even though myOtherClass does not have an explicitly declared member "aString" it can still use it in its constructor.

 

This transitive property of class members extends as far as you would like your inheritance to go, so even if a child member is of a class 47 classes deep, it is still accessible, still usable in constructors and other contexts, and still extensible if you want to invert your model to be subclasses instead of a parent class construction, even if nowhere along the line it was explicitly re-declared.

 

Helpful resources

Announcements
PA User Group

Welcome to the User Group Public Preview

Check out new user group experience and if you are a leader please create your group

secondImage

Demo Extravaganza Winner Announcement

Please join us on Wednesday, July 21st at 8a PDT. We will be announcing the Winners of the Demo Extravaganza!

V3_PVA CAmpaign Carousel.png

Community Challenge - Giveaways!

Participate in the Power Virtual Agents Community Challenge

Carousel 2021 Release Wave 2 Plan 768x460.jpg

2021 Release Wave 2 Plan

Power Platform release plan for the 2021 release wave 2 describes all new features releasing from October 2021 through March 2022.

Users online (2,189)