Skip to content

Instantly share code, notes, and snippets.

@jnicklas
Last active November 20, 2025 09:57
Show Gist options
  • Select an option

  • Save jnicklas/0a8d136d84ff9757819e0f20c34f625a to your computer and use it in GitHub Desktop.

Select an option

Save jnicklas/0a8d136d84ff9757819e0f20c34f625a to your computer and use it in GitHub Desktop.
Type safe setting of async associations in ember
import { set } from '@ember/object';
import type { AsyncBelongsTo, AsyncHasMany } from '@ember-data/model';
type ErrorMessage<T> = { __errorMessage: T };
export type AssociationSetterValueFrom<T> =
T extends AsyncBelongsTo<infer U>
? AsyncBelongsTo<U> | U | null
: T extends AsyncHasMany<infer U>
? U[]
: ErrorMessage<'Must be an AsyncBelongsTo or AsyncHasMany association'>;
// Sets an association on a model, accepting both the related model instance and
// the AsyncBelongsTo/AsyncHasMany proxy. Ember Data does not currently support
// setting associations with the proxied types, but this utility allows us to do
// so in a type-safe manner.
//
// Example usage:
//
// import { setAsyncAssociation } from 'teamtailor/utils/set-association';
//
// const job = this.store.createRecord('job');
// const pickedQuestion = this.store.createRecord('picked-question');
//
// // Both usages are valid:
// setAsyncAssociation(pickedQuestion, 'job', job);
// setAsyncAssociation(pickedQuestion, 'job', pickedQuestion.job); // job is AsyncBelongsTo<JobModel>
// setAsyncAssociation(pickedQuestion, 'job', pickedQuestion); // type error!
export function setAsyncAssociation<
M extends object,
K extends keyof M & string,
>(model: M, name: K, value: AssociationSetterValueFrom<M[K]>) {
set(model, name, value);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment