Skip to content

v3.3.0

Choose a tag to compare

@windischb windischb released this 27 Apr 14:38
· 18 commits to develop since this release

Cocoar.JsEval v3.3.0

Property-based discriminator mappings, Type.IsOneOf, and full Monaco TypeScript narrowing. LINQ predicates now handle the full range of type-guard patterns — including optional chaining.

Added

  • Type.IsOneOf(value, ['a','b',…]) — shorthand for multiple OR'd Type.Is calls. LINQ expands to OrElse chain. TypeScript emits a conditional-type overload so Monaco narrows the union (value is PersonView | CompanyView).
  • AddDiscriminatorMappings<T>(propertyName, ...) on JsEvalBuilder — property-based mappings. ("ParticipantType", ("person", typeof(PersonView)), …) generates p.ParticipantType == "person" in LINQ + Monaco narrowing; plain string values generate property equality only.
  • DiscriminatorMapping constructors for property-based discrimination: new(baseType, value, propertyName) and new(baseType, value, concreteType, propertyName).
  • JsEngine auto-registers Type global when DiscriminatorMappings are configured.
  • DefinitionBuilder.AddDiscriminatorMappings — emits declare const Type with typed Is() / IsOneOf<D>() overloads for Monaco IntelliSense.
  • Namespace-mapping fallback in Type.Is — lookup order: explicit mapping → type alias → namespace-mapped name.

Changed

  • DiscriminatorMapping is now a class (was record). Adds PropertyName and ConcreteType; IsMatch is a pre-compiled Func<object,string,bool>.
  • TsDefinitionService skips property-only mappings (null ConcreteType) when emitting Monaco narrowing overloads.

Removed

  • DiscriminatorEntry — builder helper struct removed; AddDiscriminatorMappings overloads use native C# tuple syntax directly.

Fixed

  • AND-narrowing for combined discriminator mappings. Type.Is(p, 'person') && p.Email.endsWith(...) now resolves subtype-only properties correctly for combined mappings. CollectNarrowings now recognizes p.Prop == "value" (BinaryExpression{Equal}) alongside TypeBinaryExpression.
  • Optional chaining (?.) in AND-narrowing predicates. Type.Is(p, 'person') && p.Email?.endsWith(...) now resolves subtype-only properties correctly. VisitChainElement now tries TryResolveViaIntersection on failed lookups, mirroring VisitMember. Workaround (p.Email && p.Email.endsWith(...)) no longer needed.
  • DefinitionBuilder maps collections to TypeScript array types. List<T>, IEnumerable<T>, etc. are now Array<T> in the generated .d.ts instead of System.Collections.Generic.List$1<T>. .some(), .includes(), .filter() no longer show as Monaco errors. IReadOnlyList<T> / IReadOnlyCollection<T> → ReadonlyArray<T>; Dictionary<K,V> / IDictionary<K,V> → Record<K,V>.

Example

services.AddJsEval(b => b
    .AddDiscriminatorMappings<Principal>("ParticipantType",
        ("person",  typeof(PersonView)),
        ("company", typeof(CompanyView)))
);
// All of these now work in LINQ predicates:
(p) => Type.Is(p, 'person') && p.Email.endsWith('@example.com')
(p) => Type.Is(p, 'person') && p.Email?.endsWith('@example.com')   // ← fixed in 3.3.0
(p) => Type.IsOneOf(p, ['person', 'company']) && p.DisplayName.startsWith('A')

Tests: 372 green (Engine 165, Linq 160, TypeScript 29, Modules 18).