supertypes 0.0.5 supertypes: ^0.0.5 copied to clipboard
A library for better, super records
Supertypes is a library for generating complex records. This library has been heavily inspired by TypeScript's Utility Types.
Installation #
Add supertypes
and it's companion generator to your pubspec.yaml
. The easiest way to do this is to use the command
line:
For Dart projects:
dart pub add supertypes dev:supertypes_generator dev:build_runner
For Flutter projects:
flutter pub add supertypes dev:supertypes_generator dev:build_runner
To generate the types, run build_runner
:
For Dart projects:
dart run build_runner build # Build once
dart run build_runner watch # Build continuously
For Flutter projects:
flutter pub run build_runner build # Build once
flutter pub run build_runner watch # Build continuously
Usage #
Every file containing a supertype must have the following:
It should start with a part
directive.
import 'package:supertypes/supertypes.dart';
part 'example.supertypes.dart';
It should contain a typedef
with the name of the supertype, prefixed with $
for public generated records, or _$
for private generated records.
// Generates a public record called `Example`
@superType
typedef $Example = ();
// Generates a private record called `_Example`
@superType
typedef _$Example = ();
Index #
Modifier types #
Modifier types are types that take one or more types and return a new type.
Partial #
The Partial
modifier type takes a type and makes all of it's fields optional.
import 'package:supertypes/supertypes.dart';
part 'person.supertypes.dart';
typedef Person = ({
String firstName,
String lastName,
int age,
});
// We want to make all the field nullable to support partial updates
@superType
typedef $UpdatePerson = Partial<Person>;
// This generates:
typedef UpdatePerson = ({
String? firstName,
String? lastName,
int? age,
});
WithPartial #
The WithPartial
modifier type takes a type and a list of fields to make optional.
import 'package:supertypes/supertypes.dart';
part 'person.supertypes.dart';
typedef Person = ({
int id,
String firstName,
String lastName,
int age,
});
// We want to make `id` nullable
@superType
typedef $PersonWithNullableId = WithPartial<Person, ({Partial id})>;
// This generates:
/// Generate for [$PersonWithNullableId]
typedef PersonWithNullableId = ({
int? id,
String firstName,
String lastName,
int age,
});
Required #
The Required
modifier type takes a type and makes all of it's fields required.
import 'package:supertypes/supertypes.dart';
part 'person.supertypes.dart';
// Let's say we have a person type with `lastName` nullable,
// and we can't update the type easily.
// We can instead create a new type with all fields required.
typedef Person = ({
String firstName,
String? lastName,
int age,
});
// We want to make all the field nullable to support partial updates
@superType
typedef $CreatePerson = Required<Person>;
// This generates:
typedef CreatePerson = ({
String firstName,
String lastName,
int age,
});
WithRequired #
The WithRequired
modifier type takes a type and a list of fields to make required.
import 'package:supertypes/supertypes.dart';
part 'person.supertypes.dart';
typedef Person = ({
String firstName,
String? lastName,
int age,
});
// We want to make `lastName` required
@superType
typedef $PersonWithRequiredLastName = WithRequired<Person, ({Required lastName})>;
// This generates:
typedef PersonWithRequiredLastName = ({
String firstName,
String lastName,
int age,
});
Omit #
The Omit
modifier type takes a type and a list of fields to omit from the type.
import 'package:supertypes/supertypes.dart';
part 'person.supertypes.dart';
typedef Person = ({
String firstName,
String lastName,
int age,
});
// We want to remove age from the type
@superType
typedef $PersonWithoutAge = Omit<Person, ({Omit age})>;
// This generates:
/// Generate for [$PersonWithoutAge]
typedef PersonWithoutAge = ({
String firstName,
String lastName,
});
Omitting positional fields
It's possible to also omit a positional field. For example:
typedef Person = (String name, int age);
// We want to remove the x field from the type
@superType
typedef $Age = Omit<Person, (Omit,)>; // This removes the first positional field
// This removes the second positional field.
// We use `Pick` as a placeholder for the first positional field.
@superType
typedef $Name = Omit<Person, (Pick, Omit)>;
// This generates:
/// Generate for [$Age]
typedef Age = (int,);
/// Generate for [$Name]
typedef Name = (String,);
Pick #
The Pick
modifier type takes a type and a list of fields to pick from the type and ignores the rest.
import 'package:supertypes/supertypes.dart';
part 'person.supertypes.dart';
typedef Person = ({
String firstName,
String lastName,
int age,
});
// We want to remove age from the type
@superType
typedef $PersonWithoutAge = Pick<Person, ({Pick firstName, Pick lastName})>;
// This generates:
/// Generate for [$PersonWithoutAge]
typedef PersonWithoutAge = ({
String firstName,
String lastName,
});
You can also apply a modifier type to the fields you pick:
import 'package:supertypes/supertypes.dart';
part 'person.supertypes.dart';
typedef Person = ({
String? firstName,
String? lastName,
int age,
});
// Let's make age nullable and and pick firstName as a required field
@superType
typedef $PersonWithNullableAge = Pick<Person, ({Required firstName, Partial age})>;
// This generates:
/// Generate for [$PersonWithNullableAge]
typedef PersonWithNullableAge = ({
int? age,
String firstName,
});
Picking positional fields
It's possible to also pick a positional field. For example:
typedef Person = (String name, int age);
// We want to remove the x field from the type
@superType
typedef $Name = Pick<Person, (Pick,)>; // This removes the first positional field
// This removes the second positional field.
// We use `Pick` as a placeholder for the first positional field.
@superType
typedef $Age = Pick<Person, (Omit, Pick)>;
// This generates:
/// Generate for [$Name]
typedef Name = (String,);
/// Generate for [$Age]
typedef Age = (int,);
Merge #
The Merge
modifier type takes two types and merges them into one type.
import 'package:supertypes/supertypes.dart';
part 'person.supertypes.dart';
typedef Person = ({
String firstName,
String lastName,
int age,
});
typedef Address = ({
String street,
String city,
String country,
});
// We want to merge the two types
@superType
typedef $PersonWithAddress = Merge<Person, Address>;
// This generates:
/// Generate for [$PersonWithAddress]
typedef PersonWithAddress = ({
String firstName,
String lastName,
int age,
String street,
String city,
String country,
});
Using Supertypes within another Supertype #
Supertypes can be used within other supertypes. This is useful for creating complex types that can be reused.
To use a supertype within another supertype, simply use the original type you prefixed with $
or _$
.
import 'package:supertypes/supertypes.dart';
part 'person.supertypes.dart';
typedef Person = ({
String firstName,
String lastName,
int age,
});
@superType
typedef $CreatePerson = Required<Person>;
@superType
typedef $UpdatePerson = Partial<Person>;
@superType
typedef $PersonOperation = ({
$CreatePerson create,
$UpdatePerson update,
});
// This generates:
/// Generate for [$CreatePerson]
typedef CreatePerson = ({
int age,
String firstName,
String lastName,
});
/// Generate for [$UpdatePerson]
typedef UpdatePerson = ({
int? age,
String? firstName,
String? lastName,
});
/// Generate for [$PersonOperation]
typedef PersonOperation = ({
({
int age,
String firstName,
String lastName,
}) create,
({
int? age,
String? firstName,
String? lastName,
}) update,
});
Features: #
- More modifier types
- ✅
Partial
- Make all fields nullable - ✅
Required
- Make all fields required - ✅
Omit
- Omit fields from a type - ✅
Pick
- Pick fields from a type - ✅
Merge
- Merge two types - ✅
WithRequired
- Make certain fields required - ✅
WithPartial
- Make certain fields optional Awaited
- Unwrap aFuture
type recursively
- ✅
- JSON serialization and deserialization for records
- Generate
fromJson
top level function - Generate
toJson
extension method on the record
- Generate
- Support for using classes in the supertype records by extracting their fields