UtilityTypes 1.0.5

dotnet add package UtilityTypes --version 1.0.5
                    
NuGet\Install-Package UtilityTypes -Version 1.0.5
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="UtilityTypes" Version="1.0.5" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="UtilityTypes" Version="1.0.5" />
                    
Directory.Packages.props
<PackageReference Include="UtilityTypes" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add UtilityTypes --version 1.0.5
                    
#r "nuget: UtilityTypes, 1.0.5"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package UtilityTypes@1.0.5
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=UtilityTypes&version=1.0.5
                    
Install as a Cake Addin
#tool nuget:?package=UtilityTypes&version=1.0.5
                    
Install as a Cake Tool

UtilityTypes

NuGet License: MIT

C# incremental source generator that brings TypeScript-style utility types to .NET — zero runtime cost, full IDE support, compile-time safety.


Motivation

TypeScript ships Partial<T>, Pick<T, K>, Omit<T, K>, and Required<T> as first-class language constructs. In TypeScript you write:

type UserDto    = Pick<User, "id" | "name">;
type UserPatch  = Partial<User>;
type UserPublic = Omit<User, "password" | "secret">;

These are not macro tricks — they are part of the type system. The compiler resolves them structurally, keeps them in sync with the source type, and flags stale references immediately.

C# has no equivalent. The standard pattern is to write every DTO and projection type by hand:

// Hand-written — diverges silently whenever User changes
public class UserDto {
    public int    Id   { get; set; }
    public string Name { get; set; }
}

This is repetitive and brittle. When User gains a field, UserDto does not. When User renames a property, the rename does not propagate. The mismatch builds up invisibly until it surfaces as a runtime bug or a forgotten migration.

UtilityTypes closes this gap. You declare the relationship between types, not the types themselves:

[Pick(typeof(User), nameof(User.Id), nameof(User.Name))]
public partial class UserDto { }

The rest is generated at compile time, in your IDE, with full IntelliSense — no reflection, no build scripts, no separate tooling step.


Design Philosophy

Declarative over imperative. The attribute on a type is the specification. There is no separate configuration file, no fluent builder, no XML schema. The declaration is co-located with the type it describes.

Compile-time, not runtime. All work happens during compilation via Roslyn's incremental generator API. The generated .g.cs files are ordinary C# source. The shipped NuGet contains no runtime library beyond the attribute definitions. There is no performance cost at application startup or request time.

Additive, not intrusive. The generator only adds members to your partial type. It never modifies your hand-written code. You can always write a member manually and use [MapIgnore] to suppress the generated equivalent — your code takes precedence.

Structural, not behavioral. UtilityTypes projects member declarations. It does not copy method bodies, expression trees, or any runtime logic. The generated output is a set of property and field declarations — the same code you would have written by hand, with no hidden behavior.


Scope: What This Library Does

  • Copies member declarations (properties and fields) from a source type into a target partial type.
  • Filters members by accessibility (public, private, protected, internal), scope (static / instance), and kind (get-set property, read-only property, field, etc.).
  • Transforms member types across the entire projection: make every type nullable (T?), non-nullable (T), or required.
  • Selects members by name ([Pick] includes by name; [Omit] excludes by name).
  • Adapts member format via a composable token system — change accessor visibility, force { get; init; }, add a name prefix, etc.
  • Detects source type changes via an optional SourceSignature hash. When the source type gains or loses members, the generator emits a warning with the updated hash.
  • Supports composition — multiple attributes on the same target type, chained source types, FromTypes for interface-driven name selection.
  • Covers all declaration forms: class, struct, interface, record, record struct, file-scoped namespaces.

Scope: What This Library Does NOT Do

  • Object-to-object mapping at runtime. UtilityTypes is not AutoMapper, Mapperly, or any equivalent. It does not emit MapToDto(source) methods. Use a runtime mapper for that.
  • Deep / nested type composition. Each attribute generates one flat set of member declarations. There is no recursive unwrapping of nested types, no JSON-path-style member selection.
  • Behavioral code generation. No constructors, no conversion operators, no interface implementations, no ToString() overrides.
  • Enum projection. Enums have no properties or fields in the Roslyn model; mapping an enum source yields zero members under default filters.
  • Runtime reflection. The library has no dependency on System.Reflection or System.Linq.Expressions. All metadata is consumed at compile time via Roslyn symbols.
  • ORM or persistence concerns. UtilityTypes is type-shape tooling. It has no concept of database columns, navigation properties, or serialization contracts.
  • Cross-assembly code generation. The generator runs within a single compilation. The source type and target type must be resolvable in the same compilation unit.

Installation

dotnet add package UtilityTypes

Targets netstandard2.0. Works in any .NET project that supports C# source generators (Visual Studio 2022+, Rider 2022+, dotnet build with .NET SDK 6+).


Quick Start

using UtilityTypes;

public class User {
    public int    Id       { get; set; }
    public string Name     { get; set; }
    public string Email    { get; set; }
    public string Password { get; set; }
}

// Copy all public instance properties from User
[Map(typeof(User))]
public partial class UserDto { }

// Copy only Id and Name  ←→  TypeScript: Pick<User, "Id" | "Name">
[Pick(typeof(User), nameof(User.Id), nameof(User.Name))]
public partial class UserSummary { }

// Copy everything except Password  ←→  TypeScript: Omit<User, "Password">
[Omit(typeof(User), nameof(User.Password))]
public partial class UserPublic { }

// Make all member types nullable (PATCH-style DTO)  ←→  TypeScript: Partial<User>
[Partial(typeof(User))]
public partial class UserPatch { }

The partial modifier on the target type is required — it is how the C# compiler merges your hand-written members with the generated ones.


Table of Contents

  1. Attributes
  2. Filter Options
  3. Member Declaration Formats
  4. Multiple Attributes on the Same Type
  5. Supported Target Types
  6. Output File Naming
  7. Diagnostics and Errors
  8. Edge Cases and Behavior Details
  9. Default Values Summary

1. Attributes

All four generator attributes live in namespace UtilityTypes and are AllowMultiple = true. [MapIgnore] is a companion attribute applied to hand-written members, not to the type itself.

1.1 [Map]

Copies all members that pass the filter from the source type into the target type. Equivalent to TypeScript type Target = Source.

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Interface,
    AllowMultiple = true)]
public class MapAttribute : Attribute

Constructor

MapAttribute(Type sourceType)
Parameter Type Required Description
sourceType Type Yes The type whose members are read

Named parameters (all optional — see Default Values)

Parameter Type Description
MemberDeclarationFormat string Controls how each member is written in the output
IncludeBaseTypes bool Also include members from base classes / parent interfaces
MemberAccessibilitySelection MemberAccessibilityFlags Filter by access modifier
MemberScopeSelection MemberScopeFlags Filter by static vs. instance
MemberKindSelection MemberKindFlags Filter by property/field kind
MemberTypeTransform MemberTypeTransform Transform every member's type: None / Nullable / NonNullable / Required
SourceSignature string? SHA256-based structural hash of the source type's members. When set, a mismatch triggers TU010 warning.

SourceSignature usage: leave it unset on first annotation; after a TU010 warning appears, copy the "current signature" value from the warning message and paste it here to re-establish the baseline.

[Map(typeof(User), SourceSignature = "a1b2c3d4")]
public partial class UserDto { }

Example

[Map(typeof(SourceType),
    IncludeBaseTypes = true,
    MemberKindSelection = MemberKindFlags.AnyProperty | MemberKindFlags.AnyField,
    MemberTypeTransform = MemberTypeTransform.NonNullable,
    MemberDeclarationFormat = MemberDeclarationFormats.PublicGetSetProp)]
public partial class Target { }

1.2 [Pick]

Copies only the explicitly named members from the source type. Equivalent to TypeScript Pick<Source, "a" | "b">.

public class PickAttribute : MapAttribute

Constructor

PickAttribute(Type sourceType, params string[] fields)
Parameter Type Required Description
sourceType Type Yes The type to pick from
fields string[] Yes Names of members to include (variadic / params)

Inherits all named parameters from MapAttribute, plus:

Parameter Type Description
FromTypes Type[]? Extract member names from these interfaces/types and merge them with fields (deduplicated)

Example

[Pick(typeof(User), nameof(User.Id), nameof(User.Name))]
public partial class UserSummary { }

// With options
[Pick(typeof(User), "Id", "Name",
    IncludeBaseTypes = true,
    MemberDeclarationFormat = MemberDeclarationFormats.GetProp)]
public partial class UserReadonly { }

// FromTypes: pull member names from an interface contract
interface IIdentified { int Id { get; set; } }
[Pick(typeof(User), FromTypes = new[] { typeof(IIdentified) })]
public partial class UserById { }   // equivalent to Pick(typeof(User), "Id")

Note: The fields names (including those derived from FromTypes) are matched against members that have already passed the MemberKindSelection, MemberAccessibilitySelection, and MemberScopeSelection filters. A name filtered out before the name check will not appear in the output, and diagnostic TU004 is emitted.


1.3 [Omit]

Copies all members that pass the filter except the explicitly named ones. Equivalent to TypeScript Omit<Source, "a" | "b">.

public class OmitAttribute : MapAttribute

Constructor

OmitAttribute(Type sourceType, params string[] fields)
Parameter Type Required Description
sourceType Type Yes The type to omit from
fields string[] No Names of members to exclude (empty = copy all)

Inherits all named parameters from MapAttribute, plus:

Parameter Type Description
FromTypes Type[]? Extract member names from these interfaces/types and merge them into the exclude set (deduplicated)

Example

[Omit(typeof(User), nameof(User.Password))]
public partial class UserPublic { }

// Omit with no field names = copy everything (functionally identical to Map)
[Omit(typeof(User))]
public partial class UserAll { }

// FromTypes: exclude all members defined in a "sensitive" interface contract
interface ISensitive { string Password { get; set; } string Token { get; set; } }
[Omit(typeof(User), FromTypes = new[] { typeof(ISensitive) })]
public partial class UserSafe { }   // equivalent to Omit(typeof(User), "Password", "Token")

1.4 [Partial]

Makes every selected member's type nullable. Equivalent to TypeScript Partial<T>. Optionally accepts member names (or FromTypes) to behave as Partial<Pick<T, K>>.

public class PartialAttribute : MapAttribute

Constructor

PartialAttribute(Type sourceType, params string[] fields)
Parameter Type Required Description
sourceType Type Yes The type to map from
fields string[] No If provided, only these members are mapped (Pick semantics applied first)

Inherits all named parameters from MapAttribute. MemberTypeTransform is always forced to Nullable and cannot be overridden.

Additional named parameter:

Parameter Type Description
FromTypes Type[]? Extract member names from these interfaces/types and merge into the include set (deduplicated)

Example

// All members become nullable — Partial<User>
[Partial(typeof(User))]
public partial class UserPatch { }

// Only Id and Name become nullable — Partial<Pick<User, "Id" | "Name">>
[Partial(typeof(User), nameof(User.Id), nameof(User.Name))]
public partial class UserIdNamePatch { }

// FromTypes: use an interface to declare which members to make nullable
interface IPatchable { string Name { get; set; } string Email { get; set; } }
[Partial(typeof(User), FromTypes = new[] { typeof(IPatchable) })]
public partial class UserContactPatch { }

1.5 [MapIgnore]

Applied to hand-written members inside the target partial type to suppress the generator from emitting a same-named member from the source type. Use this to avoid CS0102 duplicate-member errors when you need to override or re-declare a member manually.

[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Method,
    AllowMultiple = false, Inherited = false)]
public sealed class MapIgnoreAttribute : Attribute { }

Example

[Map(typeof(User))]
public partial class UserDto {
    // Hand-written Id with a different type — suppress the generator's User.Id
    [MapIgnore]
    public string Id => _userId.ToString();
}
// Without [MapIgnore] the generator would also emit "public int Id { get; set; }"
// causing CS0102 (duplicate member).

Matching rule: name-only, StringComparer.Ordinal. Type differences are ignored — if the hand-written member has the same name as a source member, the source member is excluded from generation.


2. Filter Options

All four attributes share the same filtering options. Filters are applied in sequence:

Accessibility → Scope → Kind → (Pick/Omit name filter)

2.1 MemberAccessibilityFlags

Controls which access modifiers are included. Values can be combined with |.

Value Meaning
Public Include public members (default)
Private Include private members
Protected Include protected members
Internal Include internal members
Any Include all of the above (Public \| Private \| Protected \| Internal)
// Include public and internal members
[Map(typeof(Source),
    MemberAccessibilitySelection = MemberAccessibilityFlags.Public | MemberAccessibilityFlags.Internal)]
public partial class Target { }

2.2 MemberScopeFlags

Controls whether instance or static members are included.

Value Meaning
Instance Non-static members only (default)
Static Static members only
Any Both static and instance
[Map(typeof(Source), MemberScopeSelection = MemberScopeFlags.Any)]
public partial class Target { }

When Static is included, the {scope} token in the format string expands to " static" (leading space). For instance members it expands to "".


2.3 MemberKindFlags

Controls which kinds of properties and fields are included. Values can be combined with |.

Properties

Value Meaning
ReadonlyProperty Properties with get but no set accessor
WriteonlyProperty Properties with set but no get accessor
GetSetProperty Properties with both get and set accessors
GetProperty ReadonlyProperty \| GetSetProperty
SetProperty WriteonlyProperty \| GetSetProperty
AnyProperty All property kinds (default)

Fields

Value Meaning
WritableField Non-readonly fields
ReadonlyField readonly fields
AnyField Both writable and readonly fields

Combined

Value Meaning
Any All properties and all fields
// Only readonly properties and readonly fields
[Map(typeof(Source),
    MemberKindSelection = MemberKindFlags.ReadonlyProperty | MemberKindFlags.ReadonlyField)]
public partial class ImmutableTarget { }

// Include fields as well as properties
[Map(typeof(Source), MemberKindSelection = MemberKindFlags.AnyProperty | MemberKindFlags.AnyField)]
public partial class AllMembersTarget { }

Note: init accessors are treated as setters. A { get; init; } property matches GetSetProperty and SetProperty.


2.4 MemberTypeTransform

An orthogonal axis that transforms every mapped member's type (or adds a modifier). Set via the MemberTypeTransform named parameter on any of the four attributes.

Value Effect
None Types are copied unchanged (default)
Nullable TT? (value types become Nullable<T>; reference types get ? NRT annotation; already-nullable types are unchanged)
NonNullable T?T (strips ?; Nullable<T> unwraps to T; already non-nullable types are unchanged)
Required Every generated member gains the required modifier (C# 11+)
// Strip nullable — map from a nullable model to a strict DTO
[Map(typeof(NullableModel), MemberTypeTransform = MemberTypeTransform.NonNullable)]
public partial class StrictDto { }

// All generated members must be set in object initializers
[Map(typeof(User), MemberTypeTransform = MemberTypeTransform.Required)]
public partial class RequiredUserDto { }

Note on [Partial]: [Partial] always forces MemberTypeTransform.Nullable regardless of what you write on the attribute. This cannot be overridden.


3. Member Declaration Formats

The MemberDeclarationFormat named parameter controls the exact text written for every mapped member.

3.1 Tokens

Tokens are placeholders inside the format string, replaced per-member at generation time.

using static UtilityTypes.Abstractions.MemberDeclarationFormats;
Token Constant Expands to
{required} Tokens.Required "required " (trailing space) if member has required modifier; "" otherwise
{accessibility} Tokens.Accessibility Access modifier: public, private, protected, etc.
{scope} Tokens.Scope " static" (leading space) for static; "" for instance
{fieldAccess} Tokens.FieldAccess " readonly" (leading space) for readonly fields; "" otherwise
{type} Tokens.Type Fully-qualified type name including generics, e.g. System.Guid
{name} Tokens.Name Member name
{accessors} Tokens.Accessors { get; set; } / { get; } / { set; } for properties; ; for fields

{required} precedes {accessibility}. {scope} and {fieldAccess} include a leading space so they can be placed immediately after {accessibility} without introducing double-spaces.

3.2 Built-in Format Constants

Defined in MemberDeclarationFormats (namespace UtilityTypes.Abstractions). All constants include {scope} so static members automatically carry the static keyword.

Preserve original accessibility

Constant Effect
Source Preserves original declaration exactly (default)
GetSetProp Forces get+set property
GetProp Forces get-only property
SetProp Forces set-only (write-only) property
GetPrivateSetProp External read-only, internal writable
GetInitProp Init-only after construction (C# 9+)

Force public

Constant Effect
PublicGetSetProp Public read+write
PublicGetProp Public read-only
PublicGetPrivateSetProp Public read, private write (common DTO)
PublicGetProtectedSetProp Public read, subclass-writable
PublicGetInternalSetProp Public read, assembly-writable
PublicGetInitProp Public immutable (record style, C# 9+)
PublicGetPrivateInitProp Constructor-only init (C# 9+)

Force required public (C# 11+)

Constant Effect
RequiredPublicGetSetProp Required public read+write
RequiredPublicGetInitProp Required public init-only

Fields

Constant Effect
Field Field declaration, preserves static / readonly
PublicField Public field, preserves static / readonly

3.3 Custom Formats

Any string using the tokens above is valid. Use $"" interpolation with the Tokens constants for compile-time safety.

using static UtilityTypes.Abstractions.MemberDeclarationFormats;

// Add "Mapped" prefix to every member name
[Map(typeof(Source),
    MemberDeclarationFormat = $"{Tokens.Accessibility} {Tokens.Type} Mapped{Tokens.Name}{Tokens.Accessors}")]
public partial class PrefixedTarget { }

// Force all mapped members to public string properties
[Map(typeof(Source),
    MemberDeclarationFormat = "public string {name} { get; set; }")]
public partial class StringTarget { }

4. Multiple Attributes on the Same Type

All attributes are AllowMultiple = true. Any number of them may be stacked on a single target type. Each attribute generates an independent output file; the C# partial mechanism merges them.

interface IA { int Id { get; set; } }
interface IB { string Name { get; set; } }

[Omit(typeof(IA))]    // → IC.omit.IA.g.cs
[Omit(typeof(IB))]    // → IC.omit.IB.g.cs
public partial interface IC { }

[Pick(typeof(User), "Id")]   // → Profile.pick.User.g.cs
[Map(typeof(Address))]       // → Profile.map.Address.g.cs
public partial class Profile { }

Rules:

  • Each attribute is processed independently; filter options and format are per-attribute.
  • Your hand-written members in the partial type are never modified.
  • Different source types can be mixed freely across attributes on the same target.
  • Mixing [Map], [Pick], [Omit], and [Partial] on the same target is allowed.
  • Two attributes that would generate a member with the same name produce a CS0102 compiler error — this is by design and is the user's responsibility to resolve (use [MapIgnore] or choose non-overlapping member sets).

5. Supported Target Types

The attributes apply to class, struct, and interface declarations. record and record struct are covered by the Class and Struct targets respectively.

[Map(typeof(Source))] public partial class       Target { }
[Map(typeof(Source))] public partial struct      Target { }
[Map(typeof(Source))] public partial interface   Target { }
[Map(typeof(Source))] public partial record      Target { }
[Map(typeof(Source))] public partial record struct Target { }

Both regular and file-scoped namespaces are supported:

namespace MyApp;                          // file-scoped — OK
namespace MyApp { partial class T { } }  // block-scoped — OK

The source type (the argument to typeof(...)) can be any resolved named type: class, struct, interface, record. Enums are valid as source types but have no properties or fields in the Roslyn model, so under default filters nothing is mapped.

partial modifier is mandatory

If the target type is missing partial, the generator emits diagnostic TU001 (error) and produces no output for that annotation.


6. Output File Naming

Each attribute application generates exactly one .g.cs file:

{TargetTypeName}.{toolName}.{SourceTypeName}.g.cs
Attribute toolName
[Map] map
[Pick] pick
[Omit] omit
[Partial] partial

Examples

Annotation Generated file
[Map(typeof(Source))] Target.map.Source.g.cs
[Pick(typeof(User), "Id")] Target.pick.User.g.cs
[Omit(typeof(Entity), "Id")] Target.omit.Entity.g.cs
[Omit(typeof(IA))] on IC IC.omit.IA.g.cs
[Omit(typeof(IB))] on IC IC.omit.IB.g.cs

7. Diagnostics and Errors

Code Severity Trigger
TU000 Warning Unexpected internal generator exception
TU001 Error Target type is missing the partial modifier
TU002 Warning Filter combination yields zero members from source type
TU003 Warning One or more [Omit] field names not found in the filtered member set
TU004 Warning One or more [Pick] / [Partial] field names not found in the filtered member set
TU005 Warning Cyclic type reference detected during chained member resolution
TU006 Warning Same source type appears in duplicate attributes on the same target type
TU010 Warning SourceSignature hash does not match the source type's current structure

TU001 — Missing partial

[Map(typeof(Source))]
public class Target { }   // ❌ TU001 error; no output generated

TU002 — No members selected

// Source has only public members; requesting private yields nothing
[Map(typeof(Source), MemberAccessibilitySelection = MemberAccessibilityFlags.Private)]
public partial class Target { }   // ⚠ TU002; generated type has no members

TU003 — Omit name not found

[Omit(typeof(Source), "NonExistent")]
public partial class Target { }   // ⚠ TU003; remaining members are still mapped

TU004 — Pick name not found

[Pick(typeof(Source), "Id", "Ghost")]
public partial class Target { }   // ⚠ TU004; only "Id" is mapped

TU005 — Cyclic type reference

[Omit(typeof(IB))]
partial interface IA { }

[Omit(typeof(IA))]
partial interface IB { }   // ⚠ TU005 — IA → IB → IA cycle detected; broken gracefully

TU006 — Duplicate attribute

[Omit(typeof(Source))]
[Omit(typeof(Source))]   // ⚠ TU006 — duplicate; this one is skipped
public partial class Target { }

TU010 — SourceSignature mismatch

[Map(typeof(User), SourceSignature = "a1b2c3d4")]
public partial class UserDto { }
// If User gains or loses a member:
// ⚠ TU010: "Source type 'User' has changed.
//            Recorded: 'a1b2c3d4', current: 'e5f6a7b8'. Update SourceSignature to suppress."

Copy the current signature from the warning and update the attribute to restore the baseline.


8. Edge Cases and Behavior Details

8.1 IncludeBaseTypes

  • Default false: only members declared directly on the source type.
  • true: walks the full inheritance / interface chain.
    • Classes/structs: walks BaseType up to (but not including) System.Object / System.ValueType.
    • Interfaces: walks AllInterfaces (full transitive set of parent interfaces).
public class Base    { public int Score { get; } }
public class Derived : Base { public string Name { get; set; } }

[Map(typeof(Derived))]                           // → Name only
[Map(typeof(Derived), IncludeBaseTypes = true)]  // → Name + Score

interface IBase { string Bar { get; set; } }
interface IA    : IBase { int Id { get; set; } }

[Omit(typeof(IA))]                              // → Id only
[Omit(typeof(IA), IncludeBaseTypes = true)]     // → Id + Bar

8.2 Accessor modifiers on properties

Mixed accessor accessibility is preserved exactly under the Source format:

public class Source {
    public Guid     Id      { get; private set; }
    public int      Value   { get; init; }
    public DateTime Created { protected get; set; }
}

8.3 Static members

Static members are excluded by default. Include them with MemberScopeFlags.Static or Any. The {scope} token produces " static" for static members.

8.4 Fields

Fields are excluded by default (MemberKindFlags.AnyProperty). Enable with MemberKindFlags.WritableField, ReadonlyField, or AnyField.

8.5 Resilient generation on attribute syntax errors

The generator recovers gracefully from malformed attribute syntax.

Scenario Behavior
typeof(MisspelledType) — unresolved Emits an empty partial skeleton; no members
Completely malformed syntax No output file generated for that annotation
Typo in named parameter name Parameter ignored; default value used
Typo in named parameter value Parameter ignored; default value used

8.6 Pick / Omit name matching

Name matching is case-sensitive (StringComparer.Ordinal). Use nameof(...) to avoid typos.

[Pick(typeof(Source), nameof(Source.Id))]  // safe
[Pick(typeof(Source), "id")]               // ⚠ TU004 — "id" ≠ "Id"

8.7 Implicitly declared members

Members synthesized by the compiler (backing fields, event accessors, record equality members, etc.) are always excluded. Only members that appear explicitly in source are eligible.

8.8 Chained source types

If the source type itself has UtilityTypes attributes, the generator automatically resolves its effective members by following the chain. This mirrors TypeScript utility type composition.

interface IBase { string Bar { get; set; } }
interface IA    : IBase { int Id { get; set; } }

[Omit(typeof(IA), IncludeBaseTypes = true)]   // → IC gets: Id, Bar
[Omit(typeof(IB))]                            // → IC gets: Name
partial interface IC { }

[Omit(typeof(IC))]   // resolves IC's effective members: Id, Bar, Name
partial class C { }

Rules:

  • Explicit members on the source type take priority over chained members.
  • Cycles (ABA) are detected at compile time, reported as TU005, and broken gracefully — no infinite recursion, no build failure.
  • Diamond references (same type reachable via multiple paths) are allowed; each distinct path is resolved once.

8.9 Duplicate attributes

Applying the same attribute with the same source type more than once is detected at the syntax layer before code generation. The first occurrence generates normally; subsequent duplicates emit TU006 and are skipped, preventing build failures from duplicate hint names.

8.10 Type names in generated output

Type names are fully-qualified display strings as reported by Roslyn (e.g. System.Guid, System.Collections.Generic.List<string>). Short names are not used to avoid ambiguity.


9. Default Values Summary

The defaults represent the most common use case: copy all public, instance, property members, preserving their original declaration with no type transformation.

Parameter Type Default
MemberDeclarationFormat string MemberDeclarationFormats.Source
IncludeBaseTypes bool false
MemberAccessibilitySelection MemberAccessibilityFlags MemberAccessibilityFlags.Public
MemberScopeSelection MemberScopeFlags MemberScopeFlags.Instance
MemberKindSelection MemberKindFlags MemberKindFlags.AnyProperty
MemberTypeTransform MemberTypeTransform MemberTypeTransform.None
SourceSignature string? null (change detection disabled)
FromTypes Type[]? null (Pick / Omit / Partial only)

Appendix A — TypeScript Parallel

TypeScript UtilityTypes C#
type D = T [Map(typeof(T))]
Pick<T, "a" \| "b"> [Pick(typeof(T), "a", "b")]
Omit<T, "a" \| "b"> [Omit(typeof(T), "a", "b")]
Partial<T> [Partial(typeof(T))]
Required<T> [Map(typeof(T), MemberTypeTransform = MemberTypeTransform.Required)]
Readonly<T> [Map(typeof(T), MemberDeclarationFormat = MemberDeclarationFormats.GetProp)]
Partial<Pick<T, "a" \| "b">> [Partial(typeof(T), "a", "b")]

Appendix B — Attribute Target Matrix

Attribute class struct interface record record struct AllowMultiple Applies to
[Map] type
[Pick] type
[Omit] type
[Partial] type
[MapIgnore] member (property / field / method)

Appendix C — MemberKindFlags Composition

ReadonlyProperty  = { get; }           (get, no set)
WriteonlyProperty = { set; }           (set, no get)
GetSetProperty    = { get; set; }      (both)

GetProperty  = ReadonlyProperty | GetSetProperty    (any property with get)
SetProperty  = WriteonlyProperty | GetSetProperty   (any property with set)
AnyProperty  = ReadonlyProperty | WriteonlyProperty | GetSetProperty

WritableField = non-readonly field
ReadonlyField = readonly field
AnyField      = WritableField | ReadonlyField

Any = AnyProperty | AnyField

Appendix D — Format Token Quick Reference

Token Instance prop Static prop Required prop Writable field Readonly field
{required} (empty) (empty) required (empty) (empty)
{accessibility} public public public public public
{scope} (empty) static (empty) (empty) (empty)
{fieldAccess} (empty) (empty) (empty) (empty) readonly
{type} System.Guid int string double string
{name} Id Counter Name count label
{accessors} { get; set; } { get; set; } { get; set; } ; ;

License

MIT

Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
.NET Core netcoreapp2.0 was computed.  netcoreapp2.1 was computed.  netcoreapp2.2 was computed.  netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.0 is compatible.  netstandard2.1 was computed. 
.NET Framework net461 was computed.  net462 was computed.  net463 was computed.  net47 was computed.  net471 was computed.  net472 was computed.  net48 was computed.  net481 was computed. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen40 was computed.  tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • .NETStandard 2.0

    • No dependencies.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.0.5 54 5/27/2026