Memberwise Constructor
In order for Codables to automatically handle your class, there is some convention you need to follow.
Codables can automatically handle classes that:
- Have no constructor
- Have a memberwise constructor
Other cases will require you to provide encode function that will return arguments matching the constructor of your class.
What is a memberwise constructor?
A memberwise constructor is a constructor that takes only one argument - an object with properties matching the structure of your class properties.
This is the same as eg Swift Codable structs constructors work.
// ✅ - this is a class with a memberwise constructor
class MemberwiseClass {
foo: string;
bar: number;
constructor(input: Pick<MemberwiseClass, "foo" | "bar">) {
this.foo = input.foo;
this.bar = input.bar;
}
}
// ❌ - this class has some custom constructor logic
// Still usable with Codables, but will need to provide instance => [foo, bar] function
class NotMemberwiseClass {
foo: string;
bar: number;
constructor(foo: string, bar: number) {
this.foo = foo;
this.bar = bar;
}
}Constructor is optional and not required for @codableClass to work. Under the hood, Codables will automatically assign properties to the instance.
Constructor are here mostly for convenience, when you manually create your instances.
@codableClass TypeScript definition will automatically detect non-memberwise classes and require you to provide encode function.
// ❌ TypeScript will complain as this class has no memberwise constructor
// You will have to provide `encode` to @codableClass decorator to make error go away
@codableClass("User")
class User {
@codable() name: string;
@codable() email: string;
constructor(public name: string, public email: string) {}
}
// ✅ This works - constructor matches memberwise pattern
@codableClass("User")
class User {
@codable() name: string;
@codable() email: string;
constructor(data: Pick<User, "name" | "email">) {
this.name = data.name;
this.email = data.email;
}
}Memberwise and MemberwiseExclude helper types
In the example above, we used Pick<User, "name" | "email"> to create a type that matches the structure of your class properties.
It can quickly get verbose, especially if you have a lot of codable properties or methods.
You can use Memberwise and MemberwiseExclude types to create a type that matches the structure of your class properties, while always excluding methods.
Memberwise<User> will require every property to be present.
@codableClass("User")
class User {
@codable() name!: string;
@codable() email!: string;
someMethod() {}
// All properties except methods
constructor(data: Memberwise<User>) {
Object.assign(this, data);
}
}Memberwise<User, "someProperty"> will require only someProperty property to be present.
@codableClass("User")
class User {
@codable() name!: string;
nonCodable!: string;
anotherNonCodable!: number;
yetAnotherNonCodable!: boolean;
someMethod() {}
// Only 'name' property (other properties are not codable)
constructor(data: Memberwise<User, "name">) {
Object.assign(this, data);
}
}MemberwiseExclude<User, "nonCodable"> will require everything except nonCodable property to be present.
@codableClass("User")
class User {
@codable() name!: string;
@codable() email!: string;
@codable() age!: number;
@codable() isAdmin!: boolean;
@codable() createdAt!: Date;
nonCodable: string;
someMethod() {}
// Everything except 'nonCodable' property
constructor(data: MemberwiseExclude<User, "nonCodable">) {
Object.assign(this, data);
}
}Custom Constructor Handling
If your constructor doesn’t match the memberwise pattern, you need to provide a function that will return arguments matching the constructor of your class.
@codableClass("User", {
encode: (user) => [user.name, user.email],
})
class User {
@codable() name: string;
@codable() email: string;
// Non-memberwise constructor
constructor(name: string, email: string) {
this.name = name;
this.email = email;
}
}