v3.3.0
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'dType.Iscalls. LINQ expands toOrElsechain. TypeScript emits a conditional-type overload so Monaco narrows the union (value is PersonView | CompanyView).AddDiscriminatorMappings<T>(propertyName, ...)onJsEvalBuilder— property-based mappings.("ParticipantType", ("person", typeof(PersonView)), …)generatesp.ParticipantType == "person"in LINQ + Monaco narrowing; plain string values generate property equality only.DiscriminatorMappingconstructors for property-based discrimination:new(baseType, value, propertyName)andnew(baseType, value, concreteType, propertyName).JsEngineauto-registersTypeglobal whenDiscriminatorMappingsare configured.DefinitionBuilder.AddDiscriminatorMappings— emitsdeclare const Typewith typedIs()/IsOneOf<D>()overloads for Monaco IntelliSense.- Namespace-mapping fallback in
Type.Is— lookup order: explicit mapping → type alias → namespace-mapped name.
Changed
DiscriminatorMappingis now aclass(wasrecord). AddsPropertyNameandConcreteType;IsMatchis a pre-compiledFunc<object,string,bool>.TsDefinitionServiceskips property-only mappings (nullConcreteType) when emitting Monaco narrowing overloads.
Removed
DiscriminatorEntry— builder helper struct removed;AddDiscriminatorMappingsoverloads 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.CollectNarrowingsnow recognizesp.Prop == "value"(BinaryExpression{Equal}) alongsideTypeBinaryExpression. - Optional chaining (
?.) in AND-narrowing predicates.Type.Is(p, 'person') && p.Email?.endsWith(...)now resolves subtype-only properties correctly.VisitChainElementnow triesTryResolveViaIntersectionon failed lookups, mirroringVisitMember. Workaround (p.Email && p.Email.endsWith(...)) no longer needed. DefinitionBuildermaps collections to TypeScript array types.List<T>,IEnumerable<T>, etc. are nowArray<T>in the generated.d.tsinstead ofSystem.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).