Abstract out the common bit from TypeScript types
— typescript, javascript — 2 min read
I often encounter situations in my TypeScript projects where I define similar looking types that share most of their structure but have slight differences. The duplication has been a dilemma for me, as I question whether I am tightly coupling types simply because they appear similar. While the issue remains unresolved, I have discovered a better approach to abstracting the common aspects. It’s using Omit<T>
utility type
// beforeexport type Note = Identifiers<NoteType> & { noteFor?: Identifiers<RecordType.ServiceUser>; createdAt?: Iso8601String; createdBy?: Identifiers<RecordType.Worker> | Worker; action?: RecordWithType<NoteAction> & { description?: string; reviewedAt?: string; reviewedBy?: Identifiers<RecordType.Worker> | Worker; /** This is only optional or undefined for compatibility with reduce_triage_volume turned off. This should be required once that * feature flag is removed. */ reviewOutcome?: string; reasonForReview?: string; }; description?: string; incident?: string; agreedAction?: string; safeguardings?: Identifiers<RecordType.Safeguarding>[];};
export type UpdateNotePayload = WithType<NoteType> & { action?: RecordWithType<NoteAction> & { description?: string; reviewedAt?: string; reviewedBy?: Identifiers<RecordType.Worker> | Worker; /** This is only optional or undefined for compatibility with reduce_triage_volume turned off. This should be required once that * feature flag is removed. */ reviewOutcome?: string; reasonForReview?: string; }; description?: string; incident?: string; agreedAction?: string; safeguardings?: Identifiers<RecordType.Safeguarding>[];}
export type CreateNotePayload = WithType<NoteType> & { noteFor?: Identifiers<RecordType.ServiceUser>; createdBy?: Identifiers<RecordType.Worker> | Worker; action?: RecordWithType<NoteAction> & { description?: string; reviewedAt?: string; reviewedBy?: Identifiers<RecordType.Worker> | Worker; /** This is only optional or undefined for compatibility with reduce_triage_volume turned off. This should be required once that * feature flag is removed. */ reviewOutcome?: string; reasonForReview?: string; }; description?: string; incident?: string; agreedAction?: string; safeguardings?: Identifiers<RecordType.Safeguarding>[];}
// afterexport type Note = Identifiers<NoteType> & { noteFor?: Identifiers<RecordType.ServiceUser>; createdAt?: Iso8601String; createdBy?: Identifiers<RecordType.Worker> | Worker; action?: RecordWithType<NoteAction> & { description?: string; reviewedAt?: string; reviewedBy?: Identifiers<RecordType.Worker> | Worker; /** This is only optional or undefined for compatibility with reduce_triage_volume turned off. This should be required once that * feature flag is removed. */ reviewOutcome?: string; reasonForReview?: string; }; description?: string; incident?: string; agreedAction?: string; safeguardings?: Identifiers<RecordType.Safeguarding>[];};
export type UpdateNotePayload = Omit<Note, 'noteFor' | 'createdAt' | 'createdBy' | '@id'>;export type CreateNotePayload = Omit<Note, 'createdAt' | '@id'>;
By utilising the Omit<T>
type, I’m able to exclude specific properties from the origin Note
type, resulting in more concise and reusable UpdateNotePayload
and CreateNotePayload
types.