Skip to content

Generate typings for multi-select picklist, file and image columns via AttributeTypeName - #333

Open
Cordedmink2 wants to merge 7 commits into
Power-Maverick:mainfrom
Cordedmink2:fix/multiselect-typings-generation
Open

Generate typings for multi-select picklist, file and image columns via AttributeTypeName#333
Cordedmink2 wants to merge 7 commits into
Power-Maverick:mainfrom
Cordedmink2:fix/multiselect-typings-generation

Conversation

@Cordedmink2

@Cordedmink2 Cordedmink2 commented Jul 15, 2026

Copy link
Copy Markdown

Problem

Generating typings for an entity with multi-select picklist (Choices) columns produces their option-value enums but no getAttribute/getControl signatures — addresses #313 (closed by the stale bot; I'm the original reporter and this delivers the PR discussed there). Root cause: generation filters on the legacy AttributeType property, where multi-select, file and image columns all report Virtual, so they were dropped before the type maps were ever consulted. Microsoft's guidance is to use AttributeTypeName for anything newer than the original 2011 type set.

Changes

  • Typing generation is re-keyed on AttributeTypeName.Value: multi-selects map to Attributes.MultiSelectOptionSetAttribute / Controls.MultiSelectOptionSetControl, booleans now get Controls.BooleanControl (previously generic StandardControl), and file/image columns are included with conservative fallback types (Attributes.Attribute / Controls.StandardControl) since @types/xrm has no dedicated interfaces for them. The docs list file/image as valid getAttributeType() values but don't document their getControlType(); these fallbacks are deliberately conservative, and I'll follow up with observed runtime values from a live form.
  • The attribute inclusion rule is unchanged in substance from the previous implementation. Only true virtual columns are excluded, alongside the existing non-customizable and _base exclusions. The exclusion keys on AttributeTypeName.Value !== "VirtualType" rather than the legacy AttributeType !== "Virtual", and that is precisely what lets multi-select, file and image columns through — all three report Virtual in the legacy enum. Primary ids and system columns such as importsequencenumber, owninguser and owningbusinessunitname are emitted exactly as before.
  • Unmapped column types still fall back to the generic Xrm types (Attributes.Attribute / Controls.StandardControl) rather than being dropped, so a column type this version doesn't know about gets a usable signature instead of vanishing from the output.
  • Option-value enums are derived from the same filtered attribute set as the signatures, so enum/signature output can no longer disagree (the inverse of the [Bug]: MultiSelectPicklistAttribute attributes fail to generate when generating typings. #313 symptom); statecode/statuscode enums resolve directly from those attributes rather than via their AttributeOf companion columns.
  • Fixed a latent race: enum-resolution promises were fired via .forEach(async ...) and never awaited before the file was emitted — they only landed in the output because the directory QuickPick stalls long enough. Resolution is now awaited (Promise.all) with a regression test.
  • Generation logic extracted to a vscode-free src/helpers/typingsBuilder.ts with mocha unit tests (npm run test:unit, 13 tests). The new script exists because the current pretest/test chain can't run: pretest calls a nonexistent compile-tests script, tsconfig.json excludes src/test/**, and runTest.ts imports the legacy vscode-test package. That harness is left untouched here; test:unit is additive and reuses the existing mocha devDependency.
  • All three project templates now require @types/xrm ^9.0.68 — the minimum version containing MultiSelectOptionSetAttribute/MultiSelectOptionSetControl (9.0.68) and BooleanControl (9.0.63). Without this, freshly generated typings don't compile in scaffolded projects. Note on the lockfile diff: the TypeScript template's package-lock.json shows ~4,800 changed lines because npm ≥7 can't write lockfileVersion: 1 — the regeneration upgrades it to v2 (readable by npm 6+). The real change is just the @types/xrm bump.

Known limitation

getAttribute/getControl return null at runtime for a column that isn't on the loaded form. Generated signatures stay non-null, matching @types/xrm@9.0.68's own base signatures and the tool's existing behavior.

Testing

  • npm run compile, npm run lint (no new warnings), and npm run test:unit (13 tests: type-map coverage, retention of primary ids and system columns, unmapped-type fallback, exclusion of non-customizable / _base / true virtual columns, sort determinism, input non-mutation, resolver scoping, race regression, emitted-signature assertions) all pass.
  • Sample generated output compiles under tsc --strict against exactly @types/xrm@9.0.68, and fails against 9.0.41 with the expected missing-member errors — confirming the template bump is required.
  • Metadata behavior verified against a live Dataverse environment: multi-select (29 columns), file (146) and image (65) columns all report legacy AttributeType = "Virtual" while reporting MultiSelectPicklistType / FileType / ImageType via AttributeTypeName; statecode/statuscode report StateType/StatusType.

Copilot AI review requested due to automatic review settings July 15, 2026 07:28

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR updates the Dataverse typings generator to key off AttributeTypeName.Value (instead of legacy AttributeType) so newer column types like multi-select picklists, file, and image columns are correctly included in generated getAttribute/getControl signatures. It also makes form-eligibility filtering use IsValidForForm, fixes an async enum-resolution race, and introduces a VS Code–independent builder with unit tests.

Changes:

  • Reworked typings generation via a new typingsBuilder helper that maps AttributeTypeName.Value to @types/xrm attribute/control types (including multi-select) and filters output using IsValidForForm.
  • Fixed enum generation ordering/race by awaiting enum-resolution work and aligned enum emission with the same filtered attribute set as signature generation.
  • Added a Node-friendly unit-test harness (tsconfig.tests.json, test:unit, mocha tests) and bumped template @types/xrm to ^9.0.68 to ensure scaffolded projects compile with the new signatures.

Reviewed changes

Copilot reviewed 9 out of 11 changed files in this pull request and generated no comments.

Show a summary per file
File Description
tsconfig.tests.json Adds a dedicated TS config for compiling mocha unit tests to out-tests.
src/utils/Interfaces.ts Extends attribute metadata typing with IsValidForForm used by the generator filter.
src/test/unit/typingsBuilder.test.ts Adds unit coverage for filtering, mapping, enum resolution awaiting, and emitted output.
src/helpers/typingsHelper.ts Switches VS Code command path to call the new builder and write emitted typings output.
src/helpers/typingsBuilder.ts New core builder: filtering, type mapping keyed by AttributeTypeName.Value, enum generation, and emission.
resources/templates/Webpack/package.json Bumps @types/xrm to a version that includes multi-select and BooleanControl typings.
resources/templates/TypeScript/package.json Bumps @types/xrm to ^9.0.68.
resources/templates/JavaScript/package.json Bumps @types/xrm to ^9.0.68.
package.json Adds test:unit script to compile/run mocha unit tests.
.gitignore Ignores the unit-test build output directory out-tests.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@Cordedmink2

Copy link
Copy Markdown
Author

@Power-Maverick This ok?

@Cordedmink2

Copy link
Copy Markdown
Author

Follow-up on the file/image note in the PR description — I observed the runtime values on existing forms in a live Dataverse environment (no columns created).

Both a generic File control and an Image control report getControlType() = "standard":

Column (type) Control classid getControlType() getAttributeType()
msdyn_soundfile.msdyn_uploadsoundfile (File) {0a7ff475-b016-4687-9ce5-042bfdbd6519} — the generic File control (19 forms in this env) standard file
msdynmkt_marketingformtemplate.entityimage (Image) {4273edbd-ac1d-40d3-9fb2-095c621b552d} (21 forms) standard image

So the conservative Controls.StandardControl fallback for file/image columns is accurate at runtime: the documented getControlType() return set has no file/image member, and both render as standard. getAttributeType() returns the documented "file"/"image" values, for which @types/xrm has no dedicated Attribute subtype — so the generic Attributes.Attribute fallback also remains appropriate. No @types/xrm change needed.

@Power-Maverick Power-Maverick left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

The enums were ok (need more review) but the list of attributes was not correct. it missed primary id field and some of the attributes like importsequencenumber, owninguser,owningbusinessunitname, etc. so it is not correct. Liked how you have a separate builder file but can you please fix the issue with the attribute generation. Thanks.

C-Adapt added 2 commits July 25, 2026 22:18
Filtering on IsValidForForm and IsPrimaryId dropped columns the previous
implementation emitted, including primary ids and system columns such as
importsequencenumber, owninguser and owningbusinessunitname.

Exclude only true VirtualType columns, matching the previous rule, and
restore the fallback to Attributes.Attribute/Controls.StandardControl so
an unrecognised column type still gets a signature instead of being
dropped. Keying the exclusion on AttributeTypeName rather than the legacy
AttributeType enum keeps multi-select, file and image columns included.

IsValidForForm is no longer read, so its addition to IAttributeDefinition
is reverted.
BigInt, CalendarRules, EntityName, ManagedProperty and Uniqueidentifier
columns were mapped in the previous implementation but absent from the
AttributeTypeName maps, so they fell through to the generic types. Restore
the mappings they had before.
Copilot AI review requested due to automatic review settings July 25, 2026 10:25
@Cordedmink2

Cordedmink2 commented Jul 25, 2026

Copy link
Copy Markdown
Author

Thanks,

Fixed in 38b97b2 and d68dd92:

  • isGeneratedAttribute now excludes only true virtual columns, matching the old rule: AttributeTypeName.Value !== "VirtualType" && IsCustomizable.Value && !LogicalName.endsWith("_base"). Keying on AttributeTypeName instead of the legacy AttributeType enum is what keeps multi-select, file and image columns included, since all three report Virtual there.
  • Restored the unmapped-type fallback. I had replaced it with a throwing requireMapped(), which combined with the allowlist meant any column type absent from the map was dropped entirely. It is now mappedOrDefault(), so an unrecognised type gets a usable generic signature instead of disappearing.
  • Restored the BigInt, CalendarRules, EntityName, ManagedProperty and Uniqueidentifier mappings that the previous maps had, so those keep the exact types they emitted before.
  • Reverted the IsValidForForm addition to IAttributeDefinition, since nothing reads it now.

Regression tests cover the specific columns you named and their generated signatures, plus the unmapped-type fallback and a check that multi-select/file/image are still included despite their legacy Virtual type. npm run test:unit passes 13 tests, and npm run compile and npm run lint are clean (the 2 remaining lint warnings are pre-existing in requestHelper.ts and EntityListView.ts).

I have also removed the claims from the PR description that primary ids, BigInt, EntityName, ManagedProperty and other non-form columns were intentionally dropped — that behaviour is gone, so the description no longer describes it.

The enum changes are untouched by this fix if you want to continue reviewing those.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 8 out of 10 changed files in this pull request and generated 2 comments.

Comment on lines +114 to +116
export function isGeneratedAttribute(a: ITypingSourceAttribute): boolean {
return a.AttributeTypeName.Value !== virtualTypeName && a.IsCustomizable.Value && !a.LogicalName.endsWith("_base");
}
Comment on lines +49 to +53
// Primary ids and system columns are not placeable on a form, so filtering
// on form eligibility silently dropped them from generated typings.
it("retains primary ids and system columns that cannot go on a form", async () => {
const source = [
attr("accountid", "UniqueidentifierType"),
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants