Created
February 2, 2022 10:46
-
-
Save Gummiees/17067f2978458294dfdea10192e4b03f to your computer and use it in GitHub Desktop.
Cool stuff you can do with types
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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