UtilityTypeGenerator 0.0.4
See the version list below for details.
dotnet add package UtilityTypeGenerator --version 0.0.4
NuGet\Install-Package UtilityTypeGenerator -Version 0.0.4
<PackageReference Include="UtilityTypeGenerator" Version="0.0.4" />
paket add UtilityTypeGenerator --version 0.0.4
#r "nuget: UtilityTypeGenerator, 0.0.4"
// Install UtilityTypeGenerator as a Cake Addin #addin nuget:?package=UtilityTypeGenerator&version=0.0.4 // Install UtilityTypeGenerator as a Cake Tool #tool nuget:?package=UtilityTypeGenerator&version=0.0.4
UtilityTypeGenerator
Generates TypeScript-like utility types for C#.
Utility types are generated types based on one or more input types. Slap the [UtilityType(selector)]
attribute on a
partial
type and the generator will generate a partial type with the same name and type (e.g., class, record, struct)
as the type with the attribute (yes, that can be different from the input type(s)!), but with the specified selector(s) applied.
For more information about utility types and how to use them, check out the TypeScript docs.
Important Note: This only generates auto-properties, no matter whether the input type's properties are auto-properties or not. This can be handy in and of itself, but computed properties are out of scope for this project.
Another important note: This is a source generator, so it only works w/ .NET 5.0+. However, I'm opinionated about using the latest stable C# SDK, so YMMV if you are running something ancient (like C# 7.3). You should really be setting
<LangVersion>latest</LangVersion>
in your projects (yes, that works with older TargetFrameworks).
Usage
- Add the UtilityTypeGenerator NuGet package to your project:
<PackageReference Include="UtilityTypeGenerator" Version="0.0.4" PrivateAssets="all" IncludeAssets="build; analyzers" />
- Add the
[UtilityType("selector")]
attribute to apartial
type, replacing"selector"
with the selector(s) of your choice.
Supported selectors
A selector is a string that specifies a verb (e.g., Pick
), one or more types or nested selectors, and (for some verbs) property names.
Verb | Syntax | Description |
---|---|---|
Import |
Import<T> |
Imports all of the properties from T (a type or selector). |
Intersection |
Intersection<T1, T2 [, T3] [...]> or Intersect<T1, T2 [, T3] [...]> |
Creates a type with the intersection of properties from T1 and T2 , etc. (types or selectors). Duplicate properties are okay, but the type of the property must be the same in both types. |
NotNull |
NotNull<T> |
Creates a type with all properties from T (a type or selector) transformed to non-nullable. |
Nullable |
Nullable<T> |
Creates a type with all properties from T (a type or selector) transformed to nullable. |
Omit |
Omit<T, Property1 [| Property2] [...]> or Omit<T, Property1 [, Property2] [...]> |
Creates a type with all properties from T (a type or selector) except the specified properties. |
Optional * |
Optional<T> |
Creates a type with all properties from T (a type or selector) stripped of the required keyword. <br>* Optional<T> behaves differently than it does in TypeScript! See below for details. |
Pick |
Pick<T, Property1 [| Property2] [...]> or Pick<T, Property1 [, Property2] [...]> |
Creates a type with only the specified properties from T (a type or selector). |
Required |
Required<T> |
Creates a type with all properties from T (a type or selector) marked as required .<br>Requires C# 11+ (or PolySharp!) |
Union |
Union<T1, T2 [, T3] [...]> |
Creates a type with the union of properties from T1 and T2 , etc. (types or selectors). Duplicate properties are okay, but the type of the property must be the same in both types. At least 2 types must be present in the selector. |
Examples
Import
public class Person
{
public Guid Id { get; set; }
public string? Name { get; set; }
public DateTimeOffset? BirthDate { get; set; }
}
[UtilityType("Import<Person>")]
public partial class Foo
{
public required string SomeOtherProperty { get; }
}
// generates:
public partial class Foo
{
public Guid Id { get; set; }
public string? Name { get; set; }
public DateTimeOffset? BirthDate { get; set; }
}
// since this is a partial class, SomeOtherProperty is also defined.
Pick
public class Person
{
public Guid Id { get; set; }
public string? Name { get; set; }
public DateTimeOffset? BirthDate { get; set; }
}
[UtilityType("Pick<Person, Name>")]
public partial class OnlyName;
// generates:
public partial class OnlyName
{
public string? Name { get; set; }
}
Omit
public class Person
{
public Guid Id { get; set; }
public string? Name { get; set; }
public DateTimeOffset? BirthDate { get; set; }
}
[UtilityType("Omit<Person, Name>")]
public partial class OmitName;
// generates:
public partial class OmitName
{
public Guid Id { get; set; }
public DateTimeOffset? BirthDate { get; set; }
}
Required
public class Person
{
public Guid Id { get; set; }
public string? Name { get; set; }
public DateTimeOffset? BirthDate { get; set; }
}
[UtilityType("Required<Person>")]
public partial class PersonRequired;
// generates:
public partial class PersonRequired
{
public Guid Id { get; set; } = default!;
public string Name { get; set; } = default!;
public DateTimeOffset BirthDate { get; set; } = default!;
}
Optional
IMPORTANT: This is different from TypeScript, where
Optional<T>
allowsundefined
values for the properties.
TIP: This should be combined with
Nullable<T>
to avoid NullReferenceExceptions for any reference type properties. Composition withPick<T>
andOmit<T>
can also be helpful.
public class Person
{
public required Guid Id { get; }
public required string? Name { get; }
public DateTimeOffset? BirthDate { get; set; }
}
[UtilityType("Optional<Person>")]
public partial class PersonOptional;
// generates:
public partial class PersonOptional
{
public Guid Id { get; set; } = default!;
public string Name { get; set; } = default!;
public DateTimeOffset? BirthDate { get; set; }
}
Nullable
public class Person
{
public Guid Id { get; set; } = Guid.NewGuid();
public string Name { get; set; } = "";
public DateTimeOffset BirthDate { get; set; } = DateTimeOffset.MinValue;
}
[UtilityType("Nullable<Person>")]
public partial class PersonWithNullableProperties;
// generates (note the default values are stripped!):
public partial class PersonWithNullableProperties
{
public Guid? Id { get; set; }
public string? Name { get; set; }
public DateTimeOffset? BirthDate { get; set; }
}
Union
TIP: If you are trying to Union just one type, use
Import<T>
instead.
Syntax: Union<T1, T2 [, T3] [...]>
Example
public class Person
{
public Guid Id { get; set; }
public string? Name { get; set; }
public DateTimeOffset? BirthDate { get; set; }
}
public class User
{
public required Guid Id { get; set; }
public required string? UserName { get; set; }
}
[UtilityType("Union<Person, User>")]
public partial class PersonAndUser;
// generates:
public partial class PersonAndUser
{
public Guid Id { get; set; }
public string? Name { get; set; }
public DateTimeOffset? BirthDate { get; set; }
public required string? UserName { get; set; }
}
Intersection
public class Person
{
public Guid Id { get; set; }
public string? Name { get; set; }
public DateTimeOffset? BirthDate { get; set; }
}
public class User
{
public required Guid Id { get; set; }
public required string? UserName { get; set; }
}
[UtilityType("Intersection<Person, User>")]
public partial class PersonAndUser;
// generates:
public partial class PersonAndUser
{
public Guid Id { get; set; }
}
A note on implementation choices
If this gets at all popular, I'll add more compiler messages, syntax highlighting & error checking (red-squiggles!), etc.
I chose to use a string argument instead of more C#-like syntax to allow for a more compact syntax that is identical in nearly every case to the TypeScript syntax. Under the covers, the generator uses ANTLR with a simple grammar to do the parsing, and extending it to support more selectors is fairly trivial.
If there's demand for a more verbose syntax, I'll consider adding it (or you can submit a PR).
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.