class Person {
firstName!: string; // required
lastName!: string; // required
isAdult = true // optional, has default
// OPTION specify only required fields
// constructor(params: Partial<Person> & Pick<Person, "firstName" | "lastName">) {
// OPTION specify only optional fields
// constructor(params: Omit<Person, "isAdult"> & Partial<Pick<Person, "isAdult">>) {
// OPTION specify required and optional fields explicitly
// constructor(params: Required<Pick<Person, "firstName" | "lastName">> & Partial<Pick<Person, "isAdult">>) {
// OPTION using a cutom build type
constructor(params: Dataclass<Person, "firstName" | "lastName">) {
Object.assign(this, params)
}
}
// we can also build custom types to simplify above constructors
type Dataclass<T, Required extends keyof T> = Partial<T> & Pick<T, Required>
let p = [
// correct
new Person({
firstName: "Bob",
lastName: "Dylan",
}),
new Person({
firstName: "Bobby",
lastName: "Dylan",
isAdult: false,
})
]
console.log(p)
// now we can use our favorite object syntax like spreading
let partialPerson = {
firstName: "Bobby",
isAdult: false,
}
let completePerson = new Person({
lastName: "Dylan",
...partialPerson,
})
// following should always be wrong. required fields are not set
new Person({
firstName: "Bob",
})
new Person({
})
new Person({
firstName: "Bobby",
lastName: undefined,
})
Last active
September 20, 2023 15:40
-
-
Save d-bucur/c717d7fe0e78a635b7bf7960f48098e0 to your computer and use it in GitHub Desktop.
Using classes and type checks on objects to support typesafe dataclass like syntax, similar to Rust structs. Combining the best of classes with the best of objects at the cost of some type complexity
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment