Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Initial setup of a generic application type #215

Merged
merged 9 commits into from
Jul 29, 2024

Conversation

DafyddLlyr
Copy link
Contributor

@DafyddLlyr DafyddLlyr commented Jul 23, 2024

What does this PR do?

  • Sets up a new schema, temporarily named "App", which models how we could make a more specific schema for various application types
  • We can vary this based on either the primary or granular application type. In the example here I'm using just the primary application type (i.e. pp from pp.full.minor.technicalDetails)
  • Uses dummy type and properties to illustrate how various primary application type based models could be used

Why is this necessary?

  • This will allow us to vary which fields are required, based on application type
  • As the schema has grown, more and more variables have been added which are application type specific. Out of necessity, many of these variables are currently optional as they're not provided on many user journey.
  • A stricter and richer schema should be both easier to work with, and more meaningful to consumers.

Next steps...

  • Test and peer-review this approach, and then work up some examples
  • Expand this approach to cover the current DigitalPlanningApplication schema

@DafyddLlyr DafyddLlyr force-pushed the dp/generic-application-type branch from be6815f to c54ab40 Compare July 23, 2024 19:56
Comment on lines 183 to 185
type ExtractPrimaryKeys<T> = T extends `${infer K}.${string}` ? K : T;

export type PrimaryApplicationTypes = ExtractPrimaryKeys<ApplicationTypeKeys>;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Before hitting on this I spent a bit of time working on constructing the list of keys using string literal types which was the approach we originally discussed.

type PrimaryAmendment = 'amendment';
type SecondaryAmendment = 'minorMaterial' | 'nonMaterial';
type Amendment =
  | PrimaryAmendment
  | `${PrimaryAmendment}.${SecondaryAmendment}`;
image

This felt like a pretty nice approach and did work (see tooltip in above screenshot), but had a few shortcomings compared to the approach using infer.

  • It's a whole lot more verbose
  • It lends itself very well to easily setting up a large number of permutations. Our list of application types doesn't match this pattern - for example the pp list has many unique combinations of secondary and tertiary keys.
  • It makes the "enum" of application types harder to parse and understand (and maintain?)

Here's the infer approach in action -

image

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've actually had to revisit this approach. Despite this being valid TypeScript, I've not found a meaningful way for ts-json-schema-generator to parse this - the combination of infer and template literal types isn't currently supported.

I've resorted to a more verbose, but perfectly workable, approach where we need to provide both the primary and granular variables.

@DafyddLlyr DafyddLlyr force-pushed the dp/generic-application-type branch from b77604b to c54ab40 Compare July 23, 2024 21:20
TPrimary extends PrimaryApplicationType,
TGranular extends ApplicationTypeKeys,
> {
applicationType: TGranular;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is required to be above or at an equal hierarchy level of anywhere TGranular or TPrimary is passed in.

This means if we need variants based on TGranular or TPrimary outside of data.application (I think we do?), we can't just keep this in data.application.applicationType.

* Base type for ApplicationData. Contains all shared properties across all application types
*/
export interface ApplicationDataBase<TGranular extends ApplicationTypeKeys> {
applicationType: TGranular;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TGranular is repeated here in order to make this change slightly less disruptive. Note that this is just the string value and not the value/description object (we could still support this!).

} from '../digitalPlanningApplication/enums/ApplicationTypes';

/**
* @internal
Copy link
Contributor Author

@DafyddLlyr DafyddLlyr Jul 24, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@internal is required to not expose generic types when building the JSON schema, and to force ts-json-schema-generator to roll this up into its nearest parent type.

Here's what happens to the JSON schema if we remove the @internal notation -

Generic type exposed in the schema...
image

...as opposed to being included in its nearest parent
image

@DafyddLlyr DafyddLlyr requested a review from a team July 24, 2024 13:57
@DafyddLlyr DafyddLlyr marked this pull request as ready for review July 24, 2024 13:57

export type WorksToTreesApplications = ApplicationSpecification<
'wtt',
Extract<ApplicationTypeKeys, 'wtt' | 'wtt.consent' | 'wtt.notice'>
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a really neat solution (thanks again @jessicamcinchak!) and again points towards a simpler way of encoding enums, e.g.

type WTTApplicationType =
  /**
   * @description Something
   */
  | "wtt"
  /**
   * @description Something else
   */
  | "wtt.consent"
  /**
   * @description Something else again
   */
  | "wtt.notice";

type ApplicationType = WTTApplicationType | PPApplicationType | PAApplicationType;

export type WorksToTreesApplications = ApplicationSpecification<
  'wtt',
  WTTApplicationType
>

This would solve the issue of missing or mismatched types, and having to repeat them here, if they were just defined once. I'll take a closer look at this in another PR.

@DafyddLlyr DafyddLlyr force-pushed the dp/generic-application-type branch from 689bd64 to 2300112 Compare July 25, 2024 14:31
@DafyddLlyr DafyddLlyr changed the title feat(wip): Initial setup of a generic application type feat: Initial setup of a generic application type Jul 26, 2024
Copy link
Member

@jessicamcinchak jessicamcinchak left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A bit hard to review this one as I think a lot of your initial comments/questions are now outdated - but this looks really good and very nice to be able to play with this setup without fully replacing existing Application schema yet - so I say ready to merge/fix forward on main whenever 👍

* (Temporary name to not clash with the existing `Application` type)
* The root specification for a planning application in England generated by a digital planning service
*/
export type App =
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: can we call this "NextApp" or "TestApp" or "PrototypeApp" or something similar that makes it clear it's work-in-progress adn not just short-hand for existing "Application" ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very good shout - will make this change before I merge.

@DafyddLlyr DafyddLlyr merged commit 1134eaf into main Jul 29, 2024
3 checks passed
@DafyddLlyr DafyddLlyr deleted the dp/generic-application-type branch July 29, 2024 11:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Status: Done
Development

Successfully merging this pull request may close these issues.

2 participants