Skip to content

Instantly share code, notes, and snippets.

@Gummiees
Created February 2, 2022 10:46
Show Gist options
  • Save Gummiees/17067f2978458294dfdea10192e4b03f to your computer and use it in GitHub Desktop.
Save Gummiees/17067f2978458294dfdea10192e4b03f to your computer and use it in GitHub Desktop.
Cool stuff you can do with types
// Similar magic I want to achieve:
type Role = 'patient' | 'clinician' | 'admin';
// No need to declare one by one!
type capitalizedRole = Capitalize<Role>;
// The example goes as follows:
interface GenericEvent {
id: string;
view: string;
}
// With interfaces
interface IPatientEvent {
readonly patient_id: string;
}
interface IClinicianEvent {
readonly clinician_id: string;
}
interface IAdminEvent {
readonly admin_id: string;
}
// With types
type TypePatientEvent = {
readonly patient_id: string;
};
type TypeClinicianEvent = {
readonly clinician_id: string;
};
type TypeAdminEvent = {
readonly admin_id: string;
};
// Both of these options look repetitive. I would like to apply the "magic" to simplify this.
// For that, we could do...
type GenericRoleEvent<Type extends Role> = {
[Properties in keyof GenericEvent as `${Type}_${Properties}`]: string;
};
type PatientEvent = GenericRoleEvent<'patient'>;
// ... but this would use all the properties from 'GenericEvent', not just 'id'.
// I do not want 'view' and any other properties it could have, I only want the ID :(
function logPatientEventNotWorking(param: PatientEvent) {
console.log('Patient ID', param.patient_id);
console.log('Patient ID', param.id); // Error: OK
console.log('Patient ID', param.admin_id); // Error: OK
console.log('Patient ID', param.patient_view); // I want this to throw error...
}
// We could use 'Exclude', like this:
type RemoveViewField = {
[Properties in keyof GenericEvent as Exclude<
Properties,
'view'
>]: GenericEvent[Properties];
};
type GenericRolePropsEvent<Type extends Role> = {
[Properties in keyof RemoveViewField as `${Type}_${Properties}`]: string;
};
// ... but we would have to do this for each property of the 'GenericEvent'.
// With all this in mind, AFAIK, we have two options:
/***** OPTION 1 *****/
// We pick only the ID property from the 'GenericEvent'
type GenericIdEvent = Pick<GenericEvent, 'id'>;
// Apply the same logic as before
type GenericRoleIdEvent<Type extends Role> = {
[Properties in keyof GenericIdEvent as `${Type}_${Properties}`]: string;
};
// We could also add 'Readonly'!
type PatientIdEvent1 = Readonly<RoleIdEvent<'patient'>>;
function logPatientEventOption1(param: PatientIdEvent1) {
console.log('Patient ID', param.patient_id);
console.log('Patient ID', param.id); // Error: OK
console.log('Patient ID', param.admin_id); // Error: OK
console.log('Patient ID', param.patient_view); // Error: OK!
}
/***** OPTION 2 *****/
// Not using 'GenericEvent' at all, and setting the ID suffix from a literal.
type RoleIdEvent<Type extends Role> = {
[_ in Type as `${Type}_id`]: string;
};
// We could also add 'Readonly'!
type PatientIdEvent = Readonly<RoleIdEvent<'patient'>>;
function logPatientEventOption1(param: PatientIdEvent) {
console.log('Patient ID', param.patient_id);
console.log('Patient ID', param.id); // Error: OK
console.log('Patient ID', param.admin_id); // Error: OK
console.log('Patient ID', param.patient_view); // Error: OK!
}
// Or, you know, for this case we could just use the interfaces or types from lines 13 or 25 respectively.
// That would probably make the code easier to maintain, even though it is not as efficient as it could.
// WDYT? Option 1, Option 2, Option Simple, or another I did not contemplate?
// What if that was a more complex case? Would option 1 or 2 be better? Are there other solutions?
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment